feat: publish FreeRTOS C FC09 card
This commit is contained in:
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
# Vendored FreeRTOS kernel subset
|
||||
|
||||
- Upstream: <https://github.com/FreeRTOS/FreeRTOS-Kernel>
|
||||
- Release: `V11.3.0`
|
||||
- Commit: `9b777ae5c5b8e9e456065a00294d1e5f5f9facf5`
|
||||
- License: MIT; see `LICENSE.md` in this directory.
|
||||
- Imported: 2026-07-14.
|
||||
|
||||
The vendored files are unmodified upstream sources. This card needs only
|
||||
`tasks.c`, `list.c`, `heap_4.c`, the GCC RISC-V port and public headers.
|
||||
Hazard3-specific configuration and trap routing live outside this directory in
|
||||
`include/` and `src/common/`.
|
||||
+887
@@ -0,0 +1,887 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
/* Standard includes. */
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
|
||||
* all the API functions to use the MPU wrappers. That should only be done when
|
||||
* task.h is included from an application file. */
|
||||
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||
|
||||
/* FreeRTOS includes. */
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "timers.h"
|
||||
#include "event_groups.h"
|
||||
|
||||
/* The MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined
|
||||
* for the header files above, but not in this file, in order to generate the
|
||||
* correct privileged Vs unprivileged linkage and placement. */
|
||||
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||
|
||||
/* This entire source file will be skipped if the application is not configured
|
||||
* to include event groups functionality. This #if is closed at the very bottom
|
||||
* of this file. If you want to include event groups then ensure
|
||||
* configUSE_EVENT_GROUPS is set to 1 in FreeRTOSConfig.h. */
|
||||
#if ( configUSE_EVENT_GROUPS == 1 )
|
||||
|
||||
typedef struct EventGroupDef_t
|
||||
{
|
||||
EventBits_t uxEventBits;
|
||||
List_t xTasksWaitingForBits; /**< List of tasks waiting for a bit to be set. */
|
||||
|
||||
#if ( configUSE_TRACE_FACILITY == 1 )
|
||||
UBaseType_t uxEventGroupNumber;
|
||||
#endif
|
||||
|
||||
#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
|
||||
uint8_t ucStaticallyAllocated; /**< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */
|
||||
#endif
|
||||
} EventGroup_t;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Test the bits set in uxCurrentEventBits to see if the wait condition is met.
|
||||
* The wait condition is defined by xWaitForAllBits. If xWaitForAllBits is
|
||||
* pdTRUE then the wait condition is met if all the bits set in uxBitsToWaitFor
|
||||
* are also set in uxCurrentEventBits. If xWaitForAllBits is pdFALSE then the
|
||||
* wait condition is met if any of the bits set in uxBitsToWaitFor are also set
|
||||
* in uxCurrentEventBits.
|
||||
*/
|
||||
static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits,
|
||||
const EventBits_t uxBitsToWaitFor,
|
||||
const BaseType_t xWaitForAllBits ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||
|
||||
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer )
|
||||
{
|
||||
EventGroup_t * pxEventBits;
|
||||
|
||||
traceENTER_xEventGroupCreateStatic( pxEventGroupBuffer );
|
||||
|
||||
/* A StaticEventGroup_t object must be provided. */
|
||||
configASSERT( pxEventGroupBuffer );
|
||||
|
||||
#if ( configASSERT_DEFINED == 1 )
|
||||
{
|
||||
/* Sanity check that the size of the structure used to declare a
|
||||
* variable of type StaticEventGroup_t equals the size of the real
|
||||
* event group structure. */
|
||||
volatile size_t xSize = sizeof( StaticEventGroup_t );
|
||||
configASSERT( xSize == sizeof( EventGroup_t ) );
|
||||
}
|
||||
#endif /* configASSERT_DEFINED */
|
||||
|
||||
/* The user has provided a statically allocated event group - use it. */
|
||||
/* MISRA Ref 11.3.1 [Misaligned access] */
|
||||
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */
|
||||
/* coverity[misra_c_2012_rule_11_3_violation] */
|
||||
pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer;
|
||||
|
||||
if( pxEventBits != NULL )
|
||||
{
|
||||
pxEventBits->uxEventBits = 0;
|
||||
vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );
|
||||
|
||||
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
|
||||
{
|
||||
/* Both static and dynamic allocation can be used, so note that
|
||||
* this event group was created statically in case the event group
|
||||
* is later deleted. */
|
||||
pxEventBits->ucStaticallyAllocated = pdTRUE;
|
||||
}
|
||||
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
|
||||
|
||||
traceEVENT_GROUP_CREATE( pxEventBits );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* xEventGroupCreateStatic should only ever be called with
|
||||
* pxEventGroupBuffer pointing to a pre-allocated (compile time
|
||||
* allocated) StaticEventGroup_t variable. */
|
||||
traceEVENT_GROUP_CREATE_FAILED();
|
||||
}
|
||||
|
||||
traceRETURN_xEventGroupCreateStatic( pxEventBits );
|
||||
|
||||
return pxEventBits;
|
||||
}
|
||||
|
||||
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
|
||||
|
||||
EventGroupHandle_t xEventGroupCreate( void )
|
||||
{
|
||||
EventGroup_t * pxEventBits;
|
||||
|
||||
traceENTER_xEventGroupCreate();
|
||||
|
||||
/* MISRA Ref 11.5.1 [Malloc memory assignment] */
|
||||
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
|
||||
/* coverity[misra_c_2012_rule_11_5_violation] */
|
||||
pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) );
|
||||
|
||||
if( pxEventBits != NULL )
|
||||
{
|
||||
pxEventBits->uxEventBits = 0;
|
||||
vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );
|
||||
|
||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||
{
|
||||
/* Both static and dynamic allocation can be used, so note this
|
||||
* event group was allocated statically in case the event group is
|
||||
* later deleted. */
|
||||
pxEventBits->ucStaticallyAllocated = pdFALSE;
|
||||
}
|
||||
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||||
|
||||
traceEVENT_GROUP_CREATE( pxEventBits );
|
||||
}
|
||||
else
|
||||
{
|
||||
traceEVENT_GROUP_CREATE_FAILED();
|
||||
}
|
||||
|
||||
traceRETURN_xEventGroupCreate( pxEventBits );
|
||||
|
||||
return pxEventBits;
|
||||
}
|
||||
|
||||
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToSet,
|
||||
const EventBits_t uxBitsToWaitFor,
|
||||
TickType_t xTicksToWait )
|
||||
{
|
||||
EventBits_t uxOriginalBitValue, uxReturn;
|
||||
EventGroup_t * pxEventBits = xEventGroup;
|
||||
BaseType_t xAlreadyYielded;
|
||||
BaseType_t xTimeoutOccurred = pdFALSE;
|
||||
|
||||
traceENTER_xEventGroupSync( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTicksToWait );
|
||||
|
||||
configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
|
||||
configASSERT( uxBitsToWaitFor != 0 );
|
||||
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
|
||||
{
|
||||
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
vTaskSuspendAll();
|
||||
{
|
||||
uxOriginalBitValue = pxEventBits->uxEventBits;
|
||||
|
||||
( void ) xEventGroupSetBits( xEventGroup, uxBitsToSet );
|
||||
|
||||
if( ( ( uxOriginalBitValue | uxBitsToSet ) & uxBitsToWaitFor ) == uxBitsToWaitFor )
|
||||
{
|
||||
/* All the rendezvous bits are now set - no need to block. */
|
||||
uxReturn = ( uxOriginalBitValue | uxBitsToSet );
|
||||
|
||||
/* Rendezvous always clear the bits. They will have been cleared
|
||||
* already unless this is the only task in the rendezvous. */
|
||||
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
|
||||
|
||||
xTicksToWait = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( xTicksToWait != ( TickType_t ) 0 )
|
||||
{
|
||||
traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor );
|
||||
|
||||
/* Store the bits that the calling task is waiting for in the
|
||||
* task's event list item so the kernel knows when a match is
|
||||
* found. Then enter the blocked state. */
|
||||
vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait );
|
||||
|
||||
/* This assignment is obsolete as uxReturn will get set after
|
||||
* the task unblocks, but some compilers mistakenly generate a
|
||||
* warning about uxReturn being returned without being set if the
|
||||
* assignment is omitted. */
|
||||
uxReturn = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The rendezvous bits were not set, but no block time was
|
||||
* specified - just return the current event bit value. */
|
||||
uxReturn = pxEventBits->uxEventBits;
|
||||
xTimeoutOccurred = pdTRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
xAlreadyYielded = xTaskResumeAll();
|
||||
|
||||
if( xTicksToWait != ( TickType_t ) 0 )
|
||||
{
|
||||
if( xAlreadyYielded == pdFALSE )
|
||||
{
|
||||
taskYIELD_WITHIN_API();
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
/* The task blocked to wait for its required bits to be set - at this
|
||||
* point either the required bits were set or the block time expired. If
|
||||
* the required bits were set they will have been stored in the task's
|
||||
* event list item, and they should now be retrieved then cleared. */
|
||||
uxReturn = uxTaskResetEventItemValue();
|
||||
|
||||
if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
|
||||
{
|
||||
/* The task timed out, just return the current event bit value. */
|
||||
taskENTER_CRITICAL();
|
||||
{
|
||||
uxReturn = pxEventBits->uxEventBits;
|
||||
|
||||
/* Although the task got here because it timed out before the
|
||||
* bits it was waiting for were set, it is possible that since it
|
||||
* unblocked another task has set the bits. If this is the case
|
||||
* then it needs to clear the bits before exiting. */
|
||||
if( ( uxReturn & uxBitsToWaitFor ) == uxBitsToWaitFor )
|
||||
{
|
||||
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
taskEXIT_CRITICAL();
|
||||
|
||||
xTimeoutOccurred = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The task unblocked because the bits were set. */
|
||||
}
|
||||
|
||||
/* Control bits might be set as the task had blocked should not be
|
||||
* returned. */
|
||||
uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
|
||||
}
|
||||
|
||||
traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred );
|
||||
|
||||
/* Prevent compiler warnings when trace macros are not used. */
|
||||
( void ) xTimeoutOccurred;
|
||||
|
||||
traceRETURN_xEventGroupSync( uxReturn );
|
||||
|
||||
return uxReturn;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToWaitFor,
|
||||
const BaseType_t xClearOnExit,
|
||||
const BaseType_t xWaitForAllBits,
|
||||
TickType_t xTicksToWait )
|
||||
{
|
||||
EventGroup_t * pxEventBits = xEventGroup;
|
||||
EventBits_t uxReturn, uxControlBits = 0;
|
||||
BaseType_t xWaitConditionMet, xAlreadyYielded;
|
||||
BaseType_t xTimeoutOccurred = pdFALSE;
|
||||
|
||||
traceENTER_xEventGroupWaitBits( xEventGroup, uxBitsToWaitFor, xClearOnExit, xWaitForAllBits, xTicksToWait );
|
||||
|
||||
/* Check the user is not attempting to wait on the bits used by the kernel
|
||||
* itself, and that at least one bit is being requested. */
|
||||
configASSERT( xEventGroup );
|
||||
configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
|
||||
configASSERT( uxBitsToWaitFor != 0 );
|
||||
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
|
||||
{
|
||||
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
vTaskSuspendAll();
|
||||
{
|
||||
const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;
|
||||
|
||||
/* Check to see if the wait condition is already met or not. */
|
||||
xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );
|
||||
|
||||
if( xWaitConditionMet != pdFALSE )
|
||||
{
|
||||
/* The wait condition has already been met so there is no need to
|
||||
* block. */
|
||||
uxReturn = uxCurrentEventBits;
|
||||
xTicksToWait = ( TickType_t ) 0;
|
||||
|
||||
/* Clear the wait bits if requested to do so. */
|
||||
if( xClearOnExit != pdFALSE )
|
||||
{
|
||||
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else if( xTicksToWait == ( TickType_t ) 0 )
|
||||
{
|
||||
/* The wait condition has not been met, but no block time was
|
||||
* specified, so just return the current value. */
|
||||
uxReturn = uxCurrentEventBits;
|
||||
xTimeoutOccurred = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The task is going to block to wait for its required bits to be
|
||||
* set. uxControlBits are used to remember the specified behaviour of
|
||||
* this call to xEventGroupWaitBits() - for use when the event bits
|
||||
* unblock the task. */
|
||||
if( xClearOnExit != pdFALSE )
|
||||
{
|
||||
uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
if( xWaitForAllBits != pdFALSE )
|
||||
{
|
||||
uxControlBits |= eventWAIT_FOR_ALL_BITS;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
/* Store the bits that the calling task is waiting for in the
|
||||
* task's event list item so the kernel knows when a match is
|
||||
* found. Then enter the blocked state. */
|
||||
vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );
|
||||
|
||||
/* This is obsolete as it will get set after the task unblocks, but
|
||||
* some compilers mistakenly generate a warning about the variable
|
||||
* being returned without being set if it is not done. */
|
||||
uxReturn = 0;
|
||||
|
||||
traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );
|
||||
}
|
||||
}
|
||||
xAlreadyYielded = xTaskResumeAll();
|
||||
|
||||
if( xTicksToWait != ( TickType_t ) 0 )
|
||||
{
|
||||
if( xAlreadyYielded == pdFALSE )
|
||||
{
|
||||
taskYIELD_WITHIN_API();
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
/* The task blocked to wait for its required bits to be set - at this
|
||||
* point either the required bits were set or the block time expired. If
|
||||
* the required bits were set they will have been stored in the task's
|
||||
* event list item, and they should now be retrieved then cleared. */
|
||||
uxReturn = uxTaskResetEventItemValue();
|
||||
|
||||
if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
|
||||
{
|
||||
taskENTER_CRITICAL();
|
||||
{
|
||||
/* The task timed out, just return the current event bit value. */
|
||||
uxReturn = pxEventBits->uxEventBits;
|
||||
|
||||
/* It is possible that the event bits were updated between this
|
||||
* task leaving the Blocked state and running again. */
|
||||
if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE )
|
||||
{
|
||||
if( xClearOnExit != pdFALSE )
|
||||
{
|
||||
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
xTimeoutOccurred = pdTRUE;
|
||||
}
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The task unblocked because the bits were set. */
|
||||
}
|
||||
|
||||
/* The task blocked so control bits may have been set. */
|
||||
uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
|
||||
}
|
||||
|
||||
traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );
|
||||
|
||||
/* Prevent compiler warnings when trace macros are not used. */
|
||||
( void ) xTimeoutOccurred;
|
||||
|
||||
traceRETURN_xEventGroupWaitBits( uxReturn );
|
||||
|
||||
return uxReturn;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToClear )
|
||||
{
|
||||
EventGroup_t * pxEventBits = xEventGroup;
|
||||
EventBits_t uxReturn;
|
||||
|
||||
traceENTER_xEventGroupClearBits( xEventGroup, uxBitsToClear );
|
||||
|
||||
/* Check the user is not attempting to clear the bits used by the kernel
|
||||
* itself. */
|
||||
configASSERT( xEventGroup );
|
||||
configASSERT( ( uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
{
|
||||
traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear );
|
||||
|
||||
/* The value returned is the event group value prior to the bits being
|
||||
* cleared. */
|
||||
uxReturn = pxEventBits->uxEventBits;
|
||||
|
||||
/* Clear the bits. */
|
||||
pxEventBits->uxEventBits &= ~uxBitsToClear;
|
||||
}
|
||||
taskEXIT_CRITICAL();
|
||||
|
||||
traceRETURN_xEventGroupClearBits( uxReturn );
|
||||
|
||||
return uxReturn;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) )
|
||||
|
||||
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToClear )
|
||||
{
|
||||
BaseType_t xReturn;
|
||||
|
||||
traceENTER_xEventGroupClearBitsFromISR( xEventGroup, uxBitsToClear );
|
||||
|
||||
traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear );
|
||||
xReturn = xTimerPendFunctionCallFromISR( &vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL );
|
||||
|
||||
traceRETURN_xEventGroupClearBitsFromISR( xReturn );
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
|
||||
#endif /* if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )
|
||||
{
|
||||
UBaseType_t uxSavedInterruptStatus;
|
||||
EventGroup_t const * const pxEventBits = xEventGroup;
|
||||
EventBits_t uxReturn;
|
||||
|
||||
traceENTER_xEventGroupGetBitsFromISR( xEventGroup );
|
||||
|
||||
/* MISRA Ref 4.7.1 [Return value shall be checked] */
|
||||
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */
|
||||
/* coverity[misra_c_2012_directive_4_7_violation] */
|
||||
uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
|
||||
{
|
||||
uxReturn = pxEventBits->uxEventBits;
|
||||
}
|
||||
taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
|
||||
|
||||
traceRETURN_xEventGroupGetBitsFromISR( uxReturn );
|
||||
|
||||
return uxReturn;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToSet )
|
||||
{
|
||||
ListItem_t * pxListItem;
|
||||
ListItem_t * pxNext;
|
||||
ListItem_t const * pxListEnd;
|
||||
List_t const * pxList;
|
||||
EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits, uxReturnBits;
|
||||
EventGroup_t * pxEventBits = xEventGroup;
|
||||
BaseType_t xMatchFound = pdFALSE;
|
||||
|
||||
traceENTER_xEventGroupSetBits( xEventGroup, uxBitsToSet );
|
||||
|
||||
/* Check the user is not attempting to set the bits used by the kernel
|
||||
* itself. */
|
||||
configASSERT( xEventGroup );
|
||||
configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
|
||||
|
||||
pxList = &( pxEventBits->xTasksWaitingForBits );
|
||||
pxListEnd = listGET_END_MARKER( pxList );
|
||||
vTaskSuspendAll();
|
||||
{
|
||||
traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet );
|
||||
|
||||
pxListItem = listGET_HEAD_ENTRY( pxList );
|
||||
|
||||
/* Set the bits. */
|
||||
pxEventBits->uxEventBits |= uxBitsToSet;
|
||||
|
||||
/* See if the new bit value should unblock any tasks. */
|
||||
while( pxListItem != pxListEnd )
|
||||
{
|
||||
pxNext = listGET_NEXT( pxListItem );
|
||||
uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );
|
||||
xMatchFound = pdFALSE;
|
||||
|
||||
/* Split the bits waited for from the control bits. */
|
||||
uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
|
||||
uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;
|
||||
|
||||
if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 )
|
||||
{
|
||||
/* Just looking for single bit being set. */
|
||||
if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 )
|
||||
{
|
||||
xMatchFound = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor )
|
||||
{
|
||||
/* All bits are set. */
|
||||
xMatchFound = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Need all bits to be set, but not all the bits were set. */
|
||||
}
|
||||
|
||||
if( xMatchFound != pdFALSE )
|
||||
{
|
||||
/* The bits match. Should the bits be cleared on exit? */
|
||||
if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 )
|
||||
{
|
||||
uxBitsToClear |= uxBitsWaitedFor;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
/* Store the actual event flag value in the task's event list
|
||||
* item before removing the task from the event list. The
|
||||
* eventUNBLOCKED_DUE_TO_BIT_SET bit is set so the task knows
|
||||
* that is was unblocked due to its required bits matching, rather
|
||||
* than because it timed out. */
|
||||
vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );
|
||||
}
|
||||
|
||||
/* Move onto the next list item. Note pxListItem->pxNext is not
|
||||
* used here as the list item may have been removed from the event list
|
||||
* and inserted into the ready/pending reading list. */
|
||||
pxListItem = pxNext;
|
||||
}
|
||||
|
||||
/* Clear any bits that matched when the eventCLEAR_EVENTS_ON_EXIT_BIT
|
||||
* bit was set in the control word. */
|
||||
pxEventBits->uxEventBits &= ~uxBitsToClear;
|
||||
|
||||
/* Snapshot resulting bits. */
|
||||
uxReturnBits = pxEventBits->uxEventBits;
|
||||
}
|
||||
( void ) xTaskResumeAll();
|
||||
|
||||
traceRETURN_xEventGroupSetBits( uxReturnBits );
|
||||
|
||||
return uxReturnBits;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
|
||||
{
|
||||
EventGroup_t * pxEventBits = xEventGroup;
|
||||
const List_t * pxTasksWaitingForBits;
|
||||
|
||||
traceENTER_vEventGroupDelete( xEventGroup );
|
||||
|
||||
configASSERT( pxEventBits );
|
||||
|
||||
pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits );
|
||||
|
||||
vTaskSuspendAll();
|
||||
{
|
||||
traceEVENT_GROUP_DELETE( xEventGroup );
|
||||
|
||||
while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 )
|
||||
{
|
||||
/* Unblock the task, returning 0 as the event list is being deleted
|
||||
* and cannot therefore have any bits set. */
|
||||
configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( const ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) );
|
||||
vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET );
|
||||
}
|
||||
}
|
||||
( void ) xTaskResumeAll();
|
||||
|
||||
#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
|
||||
{
|
||||
/* The event group can only have been allocated dynamically - free
|
||||
* it again. */
|
||||
vPortFree( pxEventBits );
|
||||
}
|
||||
#elif ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
|
||||
{
|
||||
/* The event group could have been allocated statically or
|
||||
* dynamically, so check before attempting to free the memory. */
|
||||
if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
|
||||
{
|
||||
vPortFree( pxEventBits );
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
|
||||
|
||||
traceRETURN_vEventGroupDelete();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||
BaseType_t xEventGroupGetStaticBuffer( EventGroupHandle_t xEventGroup,
|
||||
StaticEventGroup_t ** ppxEventGroupBuffer )
|
||||
{
|
||||
BaseType_t xReturn;
|
||||
EventGroup_t * pxEventBits = xEventGroup;
|
||||
|
||||
traceENTER_xEventGroupGetStaticBuffer( xEventGroup, ppxEventGroupBuffer );
|
||||
|
||||
configASSERT( pxEventBits );
|
||||
configASSERT( ppxEventGroupBuffer );
|
||||
|
||||
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
|
||||
{
|
||||
/* Check if the event group was statically allocated. */
|
||||
if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdTRUE )
|
||||
{
|
||||
/* MISRA Ref 11.3.1 [Misaligned access] */
|
||||
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */
|
||||
/* coverity[misra_c_2012_rule_11_3_violation] */
|
||||
*ppxEventGroupBuffer = ( StaticEventGroup_t * ) pxEventBits;
|
||||
xReturn = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
xReturn = pdFALSE;
|
||||
}
|
||||
}
|
||||
#else /* configSUPPORT_DYNAMIC_ALLOCATION */
|
||||
{
|
||||
/* Event group must have been statically allocated. */
|
||||
/* MISRA Ref 11.3.1 [Misaligned access] */
|
||||
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */
|
||||
/* coverity[misra_c_2012_rule_11_3_violation] */
|
||||
*ppxEventGroupBuffer = ( StaticEventGroup_t * ) pxEventBits;
|
||||
xReturn = pdTRUE;
|
||||
}
|
||||
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
|
||||
|
||||
traceRETURN_xEventGroupGetStaticBuffer( xReturn );
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* For internal use only - execute a 'set bits' command that was pended from
|
||||
* an interrupt. */
|
||||
void vEventGroupSetBitsCallback( void * pvEventGroup,
|
||||
uint32_t ulBitsToSet )
|
||||
{
|
||||
traceENTER_vEventGroupSetBitsCallback( pvEventGroup, ulBitsToSet );
|
||||
|
||||
/* MISRA Ref 11.5.4 [Callback function parameter] */
|
||||
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
|
||||
/* coverity[misra_c_2012_rule_11_5_violation] */
|
||||
( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet );
|
||||
|
||||
traceRETURN_vEventGroupSetBitsCallback();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* For internal use only - execute a 'clear bits' command that was pended from
|
||||
* an interrupt. */
|
||||
void vEventGroupClearBitsCallback( void * pvEventGroup,
|
||||
uint32_t ulBitsToClear )
|
||||
{
|
||||
traceENTER_vEventGroupClearBitsCallback( pvEventGroup, ulBitsToClear );
|
||||
|
||||
/* MISRA Ref 11.5.4 [Callback function parameter] */
|
||||
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
|
||||
/* coverity[misra_c_2012_rule_11_5_violation] */
|
||||
( void ) xEventGroupClearBits( pvEventGroup, ( EventBits_t ) ulBitsToClear );
|
||||
|
||||
traceRETURN_vEventGroupClearBitsCallback();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits,
|
||||
const EventBits_t uxBitsToWaitFor,
|
||||
const BaseType_t xWaitForAllBits )
|
||||
{
|
||||
BaseType_t xWaitConditionMet = pdFALSE;
|
||||
|
||||
if( xWaitForAllBits == pdFALSE )
|
||||
{
|
||||
/* Task only has to wait for one bit within uxBitsToWaitFor to be
|
||||
* set. Is one already set? */
|
||||
if( ( uxCurrentEventBits & uxBitsToWaitFor ) != ( EventBits_t ) 0 )
|
||||
{
|
||||
xWaitConditionMet = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Task has to wait for all the bits in uxBitsToWaitFor to be set.
|
||||
* Are they set already? */
|
||||
if( ( uxCurrentEventBits & uxBitsToWaitFor ) == uxBitsToWaitFor )
|
||||
{
|
||||
xWaitConditionMet = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
|
||||
return xWaitConditionMet;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) )
|
||||
|
||||
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToSet,
|
||||
BaseType_t * pxHigherPriorityTaskWoken )
|
||||
{
|
||||
BaseType_t xReturn;
|
||||
|
||||
traceENTER_xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken );
|
||||
|
||||
traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet );
|
||||
xReturn = xTimerPendFunctionCallFromISR( &vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken );
|
||||
|
||||
traceRETURN_xEventGroupSetBitsFromISR( xReturn );
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
|
||||
#endif /* if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( configUSE_TRACE_FACILITY == 1 )
|
||||
|
||||
UBaseType_t uxEventGroupGetNumber( void * xEventGroup )
|
||||
{
|
||||
UBaseType_t xReturn;
|
||||
|
||||
/* MISRA Ref 11.5.2 [Opaque pointer] */
|
||||
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
|
||||
/* coverity[misra_c_2012_rule_11_5_violation] */
|
||||
EventGroup_t const * pxEventBits = ( EventGroup_t * ) xEventGroup;
|
||||
|
||||
traceENTER_uxEventGroupGetNumber( xEventGroup );
|
||||
|
||||
if( xEventGroup == NULL )
|
||||
{
|
||||
xReturn = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
xReturn = pxEventBits->uxEventGroupNumber;
|
||||
}
|
||||
|
||||
traceRETURN_uxEventGroupGetNumber( xReturn );
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
|
||||
#endif /* configUSE_TRACE_FACILITY */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( configUSE_TRACE_FACILITY == 1 )
|
||||
|
||||
void vEventGroupSetNumber( void * xEventGroup,
|
||||
UBaseType_t uxEventGroupNumber )
|
||||
{
|
||||
traceENTER_vEventGroupSetNumber( xEventGroup, uxEventGroupNumber );
|
||||
|
||||
/* MISRA Ref 11.5.2 [Opaque pointer] */
|
||||
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
|
||||
/* coverity[misra_c_2012_rule_11_5_violation] */
|
||||
( ( EventGroup_t * ) xEventGroup )->uxEventGroupNumber = uxEventGroupNumber;
|
||||
|
||||
traceRETURN_vEventGroupSetNumber();
|
||||
}
|
||||
|
||||
#endif /* configUSE_TRACE_FACILITY */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* This entire source file will be skipped if the application is not configured
|
||||
* to include event groups functionality. If you want to include event groups
|
||||
* then ensure configUSE_EVENT_GROUPS is set to 1 in FreeRTOSConfig.h. */
|
||||
#endif /* configUSE_EVENT_GROUPS == 1 */
|
||||
+3359
File diff suppressed because it is too large
Load Diff
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _MSC_VER /* Visual Studio doesn't support #warning. */
|
||||
#warning The name of this file has changed to stack_macros.h. Please update your code accordingly. This source file (which has the original name) will be removed in a future release.
|
||||
#endif
|
||||
|
||||
#include "stack_macros.h"
|
||||
+427
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file atomic.h
|
||||
* @brief FreeRTOS atomic operation support.
|
||||
*
|
||||
* This file implements atomic functions by disabling interrupts globally.
|
||||
* Implementations with architecture specific atomic instructions can be
|
||||
* provided under each compiler directory.
|
||||
*
|
||||
* The atomic interface can be used in FreeRTOS tasks on all FreeRTOS ports. It
|
||||
* can also be used in Interrupt Service Routines (ISRs) on FreeRTOS ports that
|
||||
* support nested interrupts (i.e. portHAS_NESTED_INTERRUPTS is set to 1). The
|
||||
* atomic interface must not be used in ISRs on FreeRTOS ports that do not
|
||||
* support nested interrupts (i.e. portHAS_NESTED_INTERRUPTS is set to 0)
|
||||
* because ISRs on these ports cannot be interrupted and therefore, do not need
|
||||
* atomics in ISRs.
|
||||
*/
|
||||
|
||||
#ifndef ATOMIC_H
|
||||
#define ATOMIC_H
|
||||
|
||||
#ifndef INC_FREERTOS_H
|
||||
#error "include FreeRTOS.h must appear in source files before include atomic.h"
|
||||
#endif
|
||||
|
||||
/* Standard includes. */
|
||||
#include <stdint.h>
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/*
|
||||
* Port specific definitions -- entering/exiting critical section.
|
||||
* Refer template -- ./lib/FreeRTOS/portable/Compiler/Arch/portmacro.h
|
||||
*
|
||||
* Every call to ATOMIC_EXIT_CRITICAL() must be closely paired with
|
||||
* ATOMIC_ENTER_CRITICAL().
|
||||
*
|
||||
*/
|
||||
#if ( portHAS_NESTED_INTERRUPTS == 1 )
|
||||
|
||||
/* Nested interrupt scheme is supported in this port. */
|
||||
#define ATOMIC_ENTER_CRITICAL() \
|
||||
UBaseType_t uxCriticalSectionType = portSET_INTERRUPT_MASK_FROM_ISR()
|
||||
|
||||
#define ATOMIC_EXIT_CRITICAL() \
|
||||
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxCriticalSectionType )
|
||||
|
||||
#else
|
||||
|
||||
/* Nested interrupt scheme is NOT supported in this port. */
|
||||
#define ATOMIC_ENTER_CRITICAL() portENTER_CRITICAL()
|
||||
#define ATOMIC_EXIT_CRITICAL() portEXIT_CRITICAL()
|
||||
|
||||
#endif /* portSET_INTERRUPT_MASK_FROM_ISR() */
|
||||
|
||||
/*
|
||||
* Port specific definition -- "always inline".
|
||||
* Inline is compiler specific, and may not always get inlined depending on your
|
||||
* optimization level. Also, inline is considered as performance optimization
|
||||
* for atomic. Thus, if portFORCE_INLINE is not provided by portmacro.h,
|
||||
* instead of resulting error, simply define it away.
|
||||
*/
|
||||
#ifndef portFORCE_INLINE
|
||||
#define portFORCE_INLINE
|
||||
#endif
|
||||
|
||||
#define ATOMIC_COMPARE_AND_SWAP_SUCCESS 0x1U /**< Compare and swap succeeded, swapped. */
|
||||
#define ATOMIC_COMPARE_AND_SWAP_FAILURE 0x0U /**< Compare and swap failed, did not swap. */
|
||||
|
||||
/*----------------------------- Swap && CAS ------------------------------*/
|
||||
|
||||
/**
|
||||
* Atomic compare-and-swap
|
||||
*
|
||||
* @brief Performs an atomic compare-and-swap operation on the specified values.
|
||||
*
|
||||
* @param[in, out] pulDestination Pointer to memory location from where value is
|
||||
* to be loaded and checked.
|
||||
* @param[in] ulExchange If condition meets, write this value to memory.
|
||||
* @param[in] ulComparand Swap condition.
|
||||
*
|
||||
* @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
|
||||
*
|
||||
* @note This function only swaps *pulDestination with ulExchange, if previous
|
||||
* *pulDestination value equals ulComparand.
|
||||
*/
|
||||
static portFORCE_INLINE uint32_t Atomic_CompareAndSwap_u32( uint32_t volatile * pulDestination,
|
||||
uint32_t ulExchange,
|
||||
uint32_t ulComparand )
|
||||
{
|
||||
uint32_t ulReturnValue;
|
||||
|
||||
ATOMIC_ENTER_CRITICAL();
|
||||
{
|
||||
if( *pulDestination == ulComparand )
|
||||
{
|
||||
*pulDestination = ulExchange;
|
||||
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
|
||||
}
|
||||
}
|
||||
ATOMIC_EXIT_CRITICAL();
|
||||
|
||||
return ulReturnValue;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Atomic swap (pointers)
|
||||
*
|
||||
* @brief Atomically sets the address pointed to by *ppvDestination to the value
|
||||
* of *pvExchange.
|
||||
*
|
||||
* @param[in, out] ppvDestination Pointer to memory location from where a pointer
|
||||
* value is to be loaded and written back to.
|
||||
* @param[in] pvExchange Pointer value to be written to *ppvDestination.
|
||||
*
|
||||
* @return The initial value of *ppvDestination.
|
||||
*/
|
||||
static portFORCE_INLINE void * Atomic_SwapPointers_p32( void * volatile * ppvDestination,
|
||||
void * pvExchange )
|
||||
{
|
||||
void * pReturnValue;
|
||||
|
||||
ATOMIC_ENTER_CRITICAL();
|
||||
{
|
||||
pReturnValue = *ppvDestination;
|
||||
*ppvDestination = pvExchange;
|
||||
}
|
||||
ATOMIC_EXIT_CRITICAL();
|
||||
|
||||
return pReturnValue;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Atomic compare-and-swap (pointers)
|
||||
*
|
||||
* @brief Performs an atomic compare-and-swap operation on the specified pointer
|
||||
* values.
|
||||
*
|
||||
* @param[in, out] ppvDestination Pointer to memory location from where a pointer
|
||||
* value is to be loaded and checked.
|
||||
* @param[in] pvExchange If condition meets, write this value to memory.
|
||||
* @param[in] pvComparand Swap condition.
|
||||
*
|
||||
* @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
|
||||
*
|
||||
* @note This function only swaps *ppvDestination with pvExchange, if previous
|
||||
* *ppvDestination value equals pvComparand.
|
||||
*/
|
||||
static portFORCE_INLINE uint32_t Atomic_CompareAndSwapPointers_p32( void * volatile * ppvDestination,
|
||||
void * pvExchange,
|
||||
void * pvComparand )
|
||||
{
|
||||
uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
|
||||
|
||||
ATOMIC_ENTER_CRITICAL();
|
||||
{
|
||||
if( *ppvDestination == pvComparand )
|
||||
{
|
||||
*ppvDestination = pvExchange;
|
||||
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
|
||||
}
|
||||
}
|
||||
ATOMIC_EXIT_CRITICAL();
|
||||
|
||||
return ulReturnValue;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------- Arithmetic ------------------------------*/
|
||||
|
||||
/**
|
||||
* Atomic add
|
||||
*
|
||||
* @brief Atomically adds count to the value of the specified pointer points to.
|
||||
*
|
||||
* @param[in,out] pulAddend Pointer to memory location from where value is to be
|
||||
* loaded and written back to.
|
||||
* @param[in] ulCount Value to be added to *pulAddend.
|
||||
*
|
||||
* @return previous *pulAddend value.
|
||||
*/
|
||||
static portFORCE_INLINE uint32_t Atomic_Add_u32( uint32_t volatile * pulAddend,
|
||||
uint32_t ulCount )
|
||||
{
|
||||
uint32_t ulCurrent;
|
||||
|
||||
ATOMIC_ENTER_CRITICAL();
|
||||
{
|
||||
ulCurrent = *pulAddend;
|
||||
*pulAddend += ulCount;
|
||||
}
|
||||
ATOMIC_EXIT_CRITICAL();
|
||||
|
||||
return ulCurrent;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Atomic subtract
|
||||
*
|
||||
* @brief Atomically subtracts count from the value of the specified pointer
|
||||
* pointers to.
|
||||
*
|
||||
* @param[in,out] pulAddend Pointer to memory location from where value is to be
|
||||
* loaded and written back to.
|
||||
* @param[in] ulCount Value to be subtract from *pulAddend.
|
||||
*
|
||||
* @return previous *pulAddend value.
|
||||
*/
|
||||
static portFORCE_INLINE uint32_t Atomic_Subtract_u32( uint32_t volatile * pulAddend,
|
||||
uint32_t ulCount )
|
||||
{
|
||||
uint32_t ulCurrent;
|
||||
|
||||
ATOMIC_ENTER_CRITICAL();
|
||||
{
|
||||
ulCurrent = *pulAddend;
|
||||
*pulAddend -= ulCount;
|
||||
}
|
||||
ATOMIC_EXIT_CRITICAL();
|
||||
|
||||
return ulCurrent;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Atomic increment
|
||||
*
|
||||
* @brief Atomically increments the value of the specified pointer points to.
|
||||
*
|
||||
* @param[in,out] pulAddend Pointer to memory location from where value is to be
|
||||
* loaded and written back to.
|
||||
*
|
||||
* @return *pulAddend value before increment.
|
||||
*/
|
||||
static portFORCE_INLINE uint32_t Atomic_Increment_u32( uint32_t volatile * pulAddend )
|
||||
{
|
||||
uint32_t ulCurrent;
|
||||
|
||||
ATOMIC_ENTER_CRITICAL();
|
||||
{
|
||||
ulCurrent = *pulAddend;
|
||||
*pulAddend += 1;
|
||||
}
|
||||
ATOMIC_EXIT_CRITICAL();
|
||||
|
||||
return ulCurrent;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Atomic decrement
|
||||
*
|
||||
* @brief Atomically decrements the value of the specified pointer points to
|
||||
*
|
||||
* @param[in,out] pulAddend Pointer to memory location from where value is to be
|
||||
* loaded and written back to.
|
||||
*
|
||||
* @return *pulAddend value before decrement.
|
||||
*/
|
||||
static portFORCE_INLINE uint32_t Atomic_Decrement_u32( uint32_t volatile * pulAddend )
|
||||
{
|
||||
uint32_t ulCurrent;
|
||||
|
||||
ATOMIC_ENTER_CRITICAL();
|
||||
{
|
||||
ulCurrent = *pulAddend;
|
||||
*pulAddend -= 1;
|
||||
}
|
||||
ATOMIC_EXIT_CRITICAL();
|
||||
|
||||
return ulCurrent;
|
||||
}
|
||||
|
||||
/*----------------------------- Bitwise Logical ------------------------------*/
|
||||
|
||||
/**
|
||||
* Atomic OR
|
||||
*
|
||||
* @brief Performs an atomic OR operation on the specified values.
|
||||
*
|
||||
* @param [in, out] pulDestination Pointer to memory location from where value is
|
||||
* to be loaded and written back to.
|
||||
* @param [in] ulValue Value to be ORed with *pulDestination.
|
||||
*
|
||||
* @return The original value of *pulDestination.
|
||||
*/
|
||||
static portFORCE_INLINE uint32_t Atomic_OR_u32( uint32_t volatile * pulDestination,
|
||||
uint32_t ulValue )
|
||||
{
|
||||
uint32_t ulCurrent;
|
||||
|
||||
ATOMIC_ENTER_CRITICAL();
|
||||
{
|
||||
ulCurrent = *pulDestination;
|
||||
*pulDestination |= ulValue;
|
||||
}
|
||||
ATOMIC_EXIT_CRITICAL();
|
||||
|
||||
return ulCurrent;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Atomic AND
|
||||
*
|
||||
* @brief Performs an atomic AND operation on the specified values.
|
||||
*
|
||||
* @param [in, out] pulDestination Pointer to memory location from where value is
|
||||
* to be loaded and written back to.
|
||||
* @param [in] ulValue Value to be ANDed with *pulDestination.
|
||||
*
|
||||
* @return The original value of *pulDestination.
|
||||
*/
|
||||
static portFORCE_INLINE uint32_t Atomic_AND_u32( uint32_t volatile * pulDestination,
|
||||
uint32_t ulValue )
|
||||
{
|
||||
uint32_t ulCurrent;
|
||||
|
||||
ATOMIC_ENTER_CRITICAL();
|
||||
{
|
||||
ulCurrent = *pulDestination;
|
||||
*pulDestination &= ulValue;
|
||||
}
|
||||
ATOMIC_EXIT_CRITICAL();
|
||||
|
||||
return ulCurrent;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Atomic NAND
|
||||
*
|
||||
* @brief Performs an atomic NAND operation on the specified values.
|
||||
*
|
||||
* @param [in, out] pulDestination Pointer to memory location from where value is
|
||||
* to be loaded and written back to.
|
||||
* @param [in] ulValue Value to be NANDed with *pulDestination.
|
||||
*
|
||||
* @return The original value of *pulDestination.
|
||||
*/
|
||||
static portFORCE_INLINE uint32_t Atomic_NAND_u32( uint32_t volatile * pulDestination,
|
||||
uint32_t ulValue )
|
||||
{
|
||||
uint32_t ulCurrent;
|
||||
|
||||
ATOMIC_ENTER_CRITICAL();
|
||||
{
|
||||
ulCurrent = *pulDestination;
|
||||
*pulDestination = ~( ulCurrent & ulValue );
|
||||
}
|
||||
ATOMIC_EXIT_CRITICAL();
|
||||
|
||||
return ulCurrent;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Atomic XOR
|
||||
*
|
||||
* @brief Performs an atomic XOR operation on the specified values.
|
||||
*
|
||||
* @param [in, out] pulDestination Pointer to memory location from where value is
|
||||
* to be loaded and written back to.
|
||||
* @param [in] ulValue Value to be XORed with *pulDestination.
|
||||
*
|
||||
* @return The original value of *pulDestination.
|
||||
*/
|
||||
static portFORCE_INLINE uint32_t Atomic_XOR_u32( uint32_t volatile * pulDestination,
|
||||
uint32_t ulValue )
|
||||
{
|
||||
uint32_t ulCurrent;
|
||||
|
||||
ATOMIC_ENTER_CRITICAL();
|
||||
{
|
||||
ulCurrent = *pulDestination;
|
||||
*pulDestination ^= ulValue;
|
||||
}
|
||||
ATOMIC_EXIT_CRITICAL();
|
||||
|
||||
return ulCurrent;
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
#endif /* ATOMIC_H */
|
||||
+765
@@ -0,0 +1,765 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CO_ROUTINE_H
|
||||
#define CO_ROUTINE_H
|
||||
|
||||
#ifndef INC_FREERTOS_H
|
||||
#error "include FreeRTOS.h must appear in source files before include croutine.h"
|
||||
#endif
|
||||
|
||||
#include "list.h"
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/* Used to hide the implementation of the co-routine control block. The
|
||||
* control block structure however has to be included in the header due to
|
||||
* the macro implementation of the co-routine functionality. */
|
||||
typedef void * CoRoutineHandle_t;
|
||||
|
||||
/* Defines the prototype to which co-routine functions must conform. */
|
||||
typedef void (* crCOROUTINE_CODE)( CoRoutineHandle_t xHandle,
|
||||
UBaseType_t uxIndex );
|
||||
|
||||
typedef struct corCoRoutineControlBlock
|
||||
{
|
||||
crCOROUTINE_CODE pxCoRoutineFunction;
|
||||
ListItem_t xGenericListItem; /**< List item used to place the CRCB in ready and blocked queues. */
|
||||
ListItem_t xEventListItem; /**< List item used to place the CRCB in event lists. */
|
||||
UBaseType_t uxPriority; /**< The priority of the co-routine in relation to other co-routines. */
|
||||
UBaseType_t uxIndex; /**< Used to distinguish between co-routines when multiple co-routines use the same co-routine function. */
|
||||
uint16_t uxState; /**< Used internally by the co-routine implementation. */
|
||||
} CRCB_t; /* Co-routine control block. Note must be identical in size down to uxPriority with TCB_t. */
|
||||
|
||||
/**
|
||||
* croutine. h
|
||||
* @code{c}
|
||||
* BaseType_t xCoRoutineCreate(
|
||||
* crCOROUTINE_CODE pxCoRoutineCode,
|
||||
* UBaseType_t uxPriority,
|
||||
* UBaseType_t uxIndex
|
||||
* );
|
||||
* @endcode
|
||||
*
|
||||
* Create a new co-routine and add it to the list of co-routines that are
|
||||
* ready to run.
|
||||
*
|
||||
* @param pxCoRoutineCode Pointer to the co-routine function. Co-routine
|
||||
* functions require special syntax - see the co-routine section of the WEB
|
||||
* documentation for more information.
|
||||
*
|
||||
* @param uxPriority The priority with respect to other co-routines at which
|
||||
* the co-routine will run.
|
||||
*
|
||||
* @param uxIndex Used to distinguish between different co-routines that
|
||||
* execute the same function. See the example below and the co-routine section
|
||||
* of the WEB documentation for further information.
|
||||
*
|
||||
* @return pdPASS if the co-routine was successfully created and added to a ready
|
||||
* list, otherwise an error code defined with ProjDefs.h.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* // Co-routine to be created.
|
||||
* void vFlashCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||
* {
|
||||
* // Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||
* // This may not be necessary for const variables.
|
||||
* static const char cLedToFlash[ 2 ] = { 5, 6 };
|
||||
* static const TickType_t uxFlashRates[ 2 ] = { 200, 400 };
|
||||
*
|
||||
* // Must start every co-routine with a call to crSTART();
|
||||
* crSTART( xHandle );
|
||||
*
|
||||
* for( ;; )
|
||||
* {
|
||||
* // This co-routine just delays for a fixed period, then toggles
|
||||
* // an LED. Two co-routines are created using this function, so
|
||||
* // the uxIndex parameter is used to tell the co-routine which
|
||||
* // LED to flash and how int32_t to delay. This assumes xQueue has
|
||||
* // already been created.
|
||||
* vParTestToggleLED( cLedToFlash[ uxIndex ] );
|
||||
* crDELAY( xHandle, uxFlashRates[ uxIndex ] );
|
||||
* }
|
||||
*
|
||||
* // Must end every co-routine with a call to crEND();
|
||||
* crEND();
|
||||
* }
|
||||
*
|
||||
* // Function that creates two co-routines.
|
||||
* void vOtherFunction( void )
|
||||
* {
|
||||
* uint8_t ucParameterToPass;
|
||||
* TaskHandle_t xHandle;
|
||||
*
|
||||
* // Create two co-routines at priority 0. The first is given index 0
|
||||
* // so (from the code above) toggles LED 5 every 200 ticks. The second
|
||||
* // is given index 1 so toggles LED 6 every 400 ticks.
|
||||
* for( uxIndex = 0; uxIndex < 2; uxIndex++ )
|
||||
* {
|
||||
* xCoRoutineCreate( vFlashCoRoutine, 0, uxIndex );
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup xCoRoutineCreate xCoRoutineCreate
|
||||
* \ingroup Tasks
|
||||
*/
|
||||
BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode,
|
||||
UBaseType_t uxPriority,
|
||||
UBaseType_t uxIndex );
|
||||
|
||||
|
||||
/**
|
||||
* croutine. h
|
||||
* @code{c}
|
||||
* void vCoRoutineSchedule( void );
|
||||
* @endcode
|
||||
*
|
||||
* Run a co-routine.
|
||||
*
|
||||
* vCoRoutineSchedule() executes the highest priority co-routine that is able
|
||||
* to run. The co-routine will execute until it either blocks, yields or is
|
||||
* preempted by a task. Co-routines execute cooperatively so one
|
||||
* co-routine cannot be preempted by another, but can be preempted by a task.
|
||||
*
|
||||
* If an application comprises of both tasks and co-routines then
|
||||
* vCoRoutineSchedule should be called from the idle task (in an idle task
|
||||
* hook).
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* // This idle task hook will schedule a co-routine each time it is called.
|
||||
* // The rest of the idle task will execute between co-routine calls.
|
||||
* void vApplicationIdleHook( void )
|
||||
* {
|
||||
* vCoRoutineSchedule();
|
||||
* }
|
||||
*
|
||||
* // Alternatively, if you do not require any other part of the idle task to
|
||||
* // execute, the idle task hook can call vCoRoutineSchedule() within an
|
||||
* // infinite loop.
|
||||
* void vApplicationIdleHook( void )
|
||||
* {
|
||||
* for( ;; )
|
||||
* {
|
||||
* vCoRoutineSchedule();
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup vCoRoutineSchedule vCoRoutineSchedule
|
||||
* \ingroup Tasks
|
||||
*/
|
||||
void vCoRoutineSchedule( void );
|
||||
|
||||
/**
|
||||
* croutine. h
|
||||
* @code{c}
|
||||
* crSTART( CoRoutineHandle_t xHandle );
|
||||
* @endcode
|
||||
*
|
||||
* This macro MUST always be called at the start of a co-routine function.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* // Co-routine to be created.
|
||||
* void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||
* {
|
||||
* // Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||
* static int32_t ulAVariable;
|
||||
*
|
||||
* // Must start every co-routine with a call to crSTART();
|
||||
* crSTART( xHandle );
|
||||
*
|
||||
* for( ;; )
|
||||
* {
|
||||
* // Co-routine functionality goes here.
|
||||
* }
|
||||
*
|
||||
* // Must end every co-routine with a call to crEND();
|
||||
* crEND();
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup crSTART crSTART
|
||||
* \ingroup Tasks
|
||||
*/
|
||||
#define crSTART( pxCRCB ) \
|
||||
switch( ( ( CRCB_t * ) ( pxCRCB ) )->uxState ) { \
|
||||
case 0:
|
||||
|
||||
/**
|
||||
* croutine. h
|
||||
* @code{c}
|
||||
* crEND();
|
||||
* @endcode
|
||||
*
|
||||
* This macro MUST always be called at the end of a co-routine function.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* // Co-routine to be created.
|
||||
* void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||
* {
|
||||
* // Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||
* static int32_t ulAVariable;
|
||||
*
|
||||
* // Must start every co-routine with a call to crSTART();
|
||||
* crSTART( xHandle );
|
||||
*
|
||||
* for( ;; )
|
||||
* {
|
||||
* // Co-routine functionality goes here.
|
||||
* }
|
||||
*
|
||||
* // Must end every co-routine with a call to crEND();
|
||||
* crEND();
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup crSTART crSTART
|
||||
* \ingroup Tasks
|
||||
*/
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#define crEND() }
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/*
|
||||
* These macros are intended for internal use by the co-routine implementation
|
||||
* only. The macros should not be used directly by application writers.
|
||||
*/
|
||||
#define crSET_STATE0( xHandle ) \
|
||||
( ( CRCB_t * ) ( xHandle ) )->uxState = ( __LINE__ * 2 ); return; \
|
||||
case ( __LINE__ * 2 ):
|
||||
#define crSET_STATE1( xHandle ) \
|
||||
( ( CRCB_t * ) ( xHandle ) )->uxState = ( ( __LINE__ * 2 ) + 1 ); return; \
|
||||
case ( ( __LINE__ * 2 ) + 1 ):
|
||||
|
||||
/**
|
||||
* croutine. h
|
||||
* @code{c}
|
||||
* crDELAY( CoRoutineHandle_t xHandle, TickType_t xTicksToDelay );
|
||||
* @endcode
|
||||
*
|
||||
* Delay a co-routine for a fixed period of time.
|
||||
*
|
||||
* crDELAY can only be called from the co-routine function itself - not
|
||||
* from within a function called by the co-routine function. This is because
|
||||
* co-routines do not maintain their own stack.
|
||||
*
|
||||
* @param xHandle The handle of the co-routine to delay. This is the xHandle
|
||||
* parameter of the co-routine function.
|
||||
*
|
||||
* @param xTickToDelay The number of ticks that the co-routine should delay
|
||||
* for. The actual amount of time this equates to is defined by
|
||||
* configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant portTICK_PERIOD_MS
|
||||
* can be used to convert ticks to milliseconds.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* // Co-routine to be created.
|
||||
* void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||
* {
|
||||
* // Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||
* // This may not be necessary for const variables.
|
||||
* // We are to delay for 200ms.
|
||||
* static const xTickType xDelayTime = 200 / portTICK_PERIOD_MS;
|
||||
*
|
||||
* // Must start every co-routine with a call to crSTART();
|
||||
* crSTART( xHandle );
|
||||
*
|
||||
* for( ;; )
|
||||
* {
|
||||
* // Delay for 200ms.
|
||||
* crDELAY( xHandle, xDelayTime );
|
||||
*
|
||||
* // Do something here.
|
||||
* }
|
||||
*
|
||||
* // Must end every co-routine with a call to crEND();
|
||||
* crEND();
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup crDELAY crDELAY
|
||||
* \ingroup Tasks
|
||||
*/
|
||||
#define crDELAY( xHandle, xTicksToDelay ) \
|
||||
do { \
|
||||
if( ( xTicksToDelay ) > 0 ) \
|
||||
{ \
|
||||
vCoRoutineAddToDelayedList( ( xTicksToDelay ), NULL ); \
|
||||
} \
|
||||
crSET_STATE0( ( xHandle ) ); \
|
||||
} while( 0 )
|
||||
|
||||
/**
|
||||
* @code{c}
|
||||
* crQUEUE_SEND(
|
||||
* CoRoutineHandle_t xHandle,
|
||||
* QueueHandle_t pxQueue,
|
||||
* void *pvItemToQueue,
|
||||
* TickType_t xTicksToWait,
|
||||
* BaseType_t *pxResult
|
||||
* )
|
||||
* @endcode
|
||||
*
|
||||
* The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine
|
||||
* equivalent to the xQueueSend() and xQueueReceive() functions used by tasks.
|
||||
*
|
||||
* crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas
|
||||
* xQueueSend() and xQueueReceive() can only be used from tasks.
|
||||
*
|
||||
* crQUEUE_SEND can only be called from the co-routine function itself - not
|
||||
* from within a function called by the co-routine function. This is because
|
||||
* co-routines do not maintain their own stack.
|
||||
*
|
||||
* See the co-routine section of the WEB documentation for information on
|
||||
* passing data between tasks and co-routines and between ISR's and
|
||||
* co-routines.
|
||||
*
|
||||
* @param xHandle The handle of the calling co-routine. This is the xHandle
|
||||
* parameter of the co-routine function.
|
||||
*
|
||||
* @param pxQueue The handle of the queue on which the data will be posted.
|
||||
* The handle is obtained as the return value when the queue is created using
|
||||
* the xQueueCreate() API function.
|
||||
*
|
||||
* @param pvItemToQueue A pointer to the data being posted onto the queue.
|
||||
* The number of bytes of each queued item is specified when the queue is
|
||||
* created. This number of bytes is copied from pvItemToQueue into the queue
|
||||
* itself.
|
||||
*
|
||||
* @param xTickToDelay The number of ticks that the co-routine should block
|
||||
* to wait for space to become available on the queue, should space not be
|
||||
* available immediately. The actual amount of time this equates to is defined
|
||||
* by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant
|
||||
* portTICK_PERIOD_MS can be used to convert ticks to milliseconds (see example
|
||||
* below).
|
||||
*
|
||||
* @param pxResult The variable pointed to by pxResult will be set to pdPASS if
|
||||
* data was successfully posted onto the queue, otherwise it will be set to an
|
||||
* error defined within ProjDefs.h.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* // Co-routine function that blocks for a fixed period then posts a number onto
|
||||
* // a queue.
|
||||
* static void prvCoRoutineFlashTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||
* {
|
||||
* // Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||
* static BaseType_t xNumberToPost = 0;
|
||||
* static BaseType_t xResult;
|
||||
*
|
||||
* // Co-routines must begin with a call to crSTART().
|
||||
* crSTART( xHandle );
|
||||
*
|
||||
* for( ;; )
|
||||
* {
|
||||
* // This assumes the queue has already been created.
|
||||
* crQUEUE_SEND( xHandle, xCoRoutineQueue, &xNumberToPost, NO_DELAY, &xResult );
|
||||
*
|
||||
* if( xResult != pdPASS )
|
||||
* {
|
||||
* // The message was not posted!
|
||||
* }
|
||||
*
|
||||
* // Increment the number to be posted onto the queue.
|
||||
* xNumberToPost++;
|
||||
*
|
||||
* // Delay for 100 ticks.
|
||||
* crDELAY( xHandle, 100 );
|
||||
* }
|
||||
*
|
||||
* // Co-routines must end with a call to crEND().
|
||||
* crEND();
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup crQUEUE_SEND crQUEUE_SEND
|
||||
* \ingroup Tasks
|
||||
*/
|
||||
#define crQUEUE_SEND( xHandle, pxQueue, pvItemToQueue, xTicksToWait, pxResult ) \
|
||||
do { \
|
||||
*( pxResult ) = xQueueCRSend( ( pxQueue ), ( pvItemToQueue ), ( xTicksToWait ) ); \
|
||||
if( *( pxResult ) == errQUEUE_BLOCKED ) \
|
||||
{ \
|
||||
crSET_STATE0( ( xHandle ) ); \
|
||||
*pxResult = xQueueCRSend( ( pxQueue ), ( pvItemToQueue ), 0 ); \
|
||||
} \
|
||||
if( *pxResult == errQUEUE_YIELD ) \
|
||||
{ \
|
||||
crSET_STATE1( ( xHandle ) ); \
|
||||
*pxResult = pdPASS; \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
/**
|
||||
* croutine. h
|
||||
* @code{c}
|
||||
* crQUEUE_RECEIVE(
|
||||
* CoRoutineHandle_t xHandle,
|
||||
* QueueHandle_t pxQueue,
|
||||
* void *pvBuffer,
|
||||
* TickType_t xTicksToWait,
|
||||
* BaseType_t *pxResult
|
||||
* )
|
||||
* @endcode
|
||||
*
|
||||
* The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine
|
||||
* equivalent to the xQueueSend() and xQueueReceive() functions used by tasks.
|
||||
*
|
||||
* crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas
|
||||
* xQueueSend() and xQueueReceive() can only be used from tasks.
|
||||
*
|
||||
* crQUEUE_RECEIVE can only be called from the co-routine function itself - not
|
||||
* from within a function called by the co-routine function. This is because
|
||||
* co-routines do not maintain their own stack.
|
||||
*
|
||||
* See the co-routine section of the WEB documentation for information on
|
||||
* passing data between tasks and co-routines and between ISR's and
|
||||
* co-routines.
|
||||
*
|
||||
* @param xHandle The handle of the calling co-routine. This is the xHandle
|
||||
* parameter of the co-routine function.
|
||||
*
|
||||
* @param pxQueue The handle of the queue from which the data will be received.
|
||||
* The handle is obtained as the return value when the queue is created using
|
||||
* the xQueueCreate() API function.
|
||||
*
|
||||
* @param pvBuffer The buffer into which the received item is to be copied.
|
||||
* The number of bytes of each queued item is specified when the queue is
|
||||
* created. This number of bytes is copied into pvBuffer.
|
||||
*
|
||||
* @param xTickToDelay The number of ticks that the co-routine should block
|
||||
* to wait for data to become available from the queue, should data not be
|
||||
* available immediately. The actual amount of time this equates to is defined
|
||||
* by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant
|
||||
* portTICK_PERIOD_MS can be used to convert ticks to milliseconds (see the
|
||||
* crQUEUE_SEND example).
|
||||
*
|
||||
* @param pxResult The variable pointed to by pxResult will be set to pdPASS if
|
||||
* data was successfully retrieved from the queue, otherwise it will be set to
|
||||
* an error code as defined within ProjDefs.h.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* // A co-routine receives the number of an LED to flash from a queue. It
|
||||
* // blocks on the queue until the number is received.
|
||||
* static void prvCoRoutineFlashWorkTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||
* {
|
||||
* // Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||
* static BaseType_t xResult;
|
||||
* static UBaseType_t uxLEDToFlash;
|
||||
*
|
||||
* // All co-routines must start with a call to crSTART().
|
||||
* crSTART( xHandle );
|
||||
*
|
||||
* for( ;; )
|
||||
* {
|
||||
* // Wait for data to become available on the queue.
|
||||
* crQUEUE_RECEIVE( xHandle, xCoRoutineQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
|
||||
*
|
||||
* if( xResult == pdPASS )
|
||||
* {
|
||||
* // We received the LED to flash - flash it!
|
||||
* vParTestToggleLED( uxLEDToFlash );
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* crEND();
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup crQUEUE_RECEIVE crQUEUE_RECEIVE
|
||||
* \ingroup Tasks
|
||||
*/
|
||||
#define crQUEUE_RECEIVE( xHandle, pxQueue, pvBuffer, xTicksToWait, pxResult ) \
|
||||
do { \
|
||||
*( pxResult ) = xQueueCRReceive( ( pxQueue ), ( pvBuffer ), ( xTicksToWait ) ); \
|
||||
if( *( pxResult ) == errQUEUE_BLOCKED ) \
|
||||
{ \
|
||||
crSET_STATE0( ( xHandle ) ); \
|
||||
*( pxResult ) = xQueueCRReceive( ( pxQueue ), ( pvBuffer ), 0 ); \
|
||||
} \
|
||||
if( *( pxResult ) == errQUEUE_YIELD ) \
|
||||
{ \
|
||||
crSET_STATE1( ( xHandle ) ); \
|
||||
*( pxResult ) = pdPASS; \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
/**
|
||||
* croutine. h
|
||||
* @code{c}
|
||||
* crQUEUE_SEND_FROM_ISR(
|
||||
* QueueHandle_t pxQueue,
|
||||
* void *pvItemToQueue,
|
||||
* BaseType_t xCoRoutinePreviouslyWoken
|
||||
* )
|
||||
* @endcode
|
||||
*
|
||||
* The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the
|
||||
* co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR()
|
||||
* functions used by tasks.
|
||||
*
|
||||
* crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to
|
||||
* pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and
|
||||
* xQueueReceiveFromISR() can only be used to pass data between a task and and
|
||||
* ISR.
|
||||
*
|
||||
* crQUEUE_SEND_FROM_ISR can only be called from an ISR to send data to a queue
|
||||
* that is being used from within a co-routine.
|
||||
*
|
||||
* See the co-routine section of the WEB documentation for information on
|
||||
* passing data between tasks and co-routines and between ISR's and
|
||||
* co-routines.
|
||||
*
|
||||
* @param xQueue The handle to the queue on which the item is to be posted.
|
||||
*
|
||||
* @param pvItemToQueue A pointer to the item that is to be placed on the
|
||||
* queue. The size of the items the queue will hold was defined when the
|
||||
* queue was created, so this many bytes will be copied from pvItemToQueue
|
||||
* into the queue storage area.
|
||||
*
|
||||
* @param xCoRoutinePreviouslyWoken This is included so an ISR can post onto
|
||||
* the same queue multiple times from a single interrupt. The first call
|
||||
* should always pass in pdFALSE. Subsequent calls should pass in
|
||||
* the value returned from the previous call.
|
||||
*
|
||||
* @return pdTRUE if a co-routine was woken by posting onto the queue. This is
|
||||
* used by the ISR to determine if a context switch may be required following
|
||||
* the ISR.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* // A co-routine that blocks on a queue waiting for characters to be received.
|
||||
* static void vReceivingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||
* {
|
||||
* char cRxedChar;
|
||||
* BaseType_t xResult;
|
||||
*
|
||||
* // All co-routines must start with a call to crSTART().
|
||||
* crSTART( xHandle );
|
||||
*
|
||||
* for( ;; )
|
||||
* {
|
||||
* // Wait for data to become available on the queue. This assumes the
|
||||
* // queue xCommsRxQueue has already been created!
|
||||
* crQUEUE_RECEIVE( xHandle, xCommsRxQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
|
||||
*
|
||||
* // Was a character received?
|
||||
* if( xResult == pdPASS )
|
||||
* {
|
||||
* // Process the character here.
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // All co-routines must end with a call to crEND().
|
||||
* crEND();
|
||||
* }
|
||||
*
|
||||
* // An ISR that uses a queue to send characters received on a serial port to
|
||||
* // a co-routine.
|
||||
* void vUART_ISR( void )
|
||||
* {
|
||||
* char cRxedChar;
|
||||
* BaseType_t xCRWokenByPost = pdFALSE;
|
||||
*
|
||||
* // We loop around reading characters until there are none left in the UART.
|
||||
* while( UART_RX_REG_NOT_EMPTY() )
|
||||
* {
|
||||
* // Obtain the character from the UART.
|
||||
* cRxedChar = UART_RX_REG;
|
||||
*
|
||||
* // Post the character onto a queue. xCRWokenByPost will be pdFALSE
|
||||
* // the first time around the loop. If the post causes a co-routine
|
||||
* // to be woken (unblocked) then xCRWokenByPost will be set to pdTRUE.
|
||||
* // In this manner we can ensure that if more than one co-routine is
|
||||
* // blocked on the queue only one is woken by this ISR no matter how
|
||||
* // many characters are posted to the queue.
|
||||
* xCRWokenByPost = crQUEUE_SEND_FROM_ISR( xCommsRxQueue, &cRxedChar, xCRWokenByPost );
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup crQUEUE_SEND_FROM_ISR crQUEUE_SEND_FROM_ISR
|
||||
* \ingroup Tasks
|
||||
*/
|
||||
#define crQUEUE_SEND_FROM_ISR( pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) \
|
||||
xQueueCRSendFromISR( ( pxQueue ), ( pvItemToQueue ), ( xCoRoutinePreviouslyWoken ) )
|
||||
|
||||
|
||||
/**
|
||||
* croutine. h
|
||||
* @code{c}
|
||||
* crQUEUE_SEND_FROM_ISR(
|
||||
* QueueHandle_t pxQueue,
|
||||
* void *pvBuffer,
|
||||
* BaseType_t * pxCoRoutineWoken
|
||||
* )
|
||||
* @endcode
|
||||
*
|
||||
* The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the
|
||||
* co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR()
|
||||
* functions used by tasks.
|
||||
*
|
||||
* crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to
|
||||
* pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and
|
||||
* xQueueReceiveFromISR() can only be used to pass data between a task and and
|
||||
* ISR.
|
||||
*
|
||||
* crQUEUE_RECEIVE_FROM_ISR can only be called from an ISR to receive data
|
||||
* from a queue that is being used from within a co-routine (a co-routine
|
||||
* posted to the queue).
|
||||
*
|
||||
* See the co-routine section of the WEB documentation for information on
|
||||
* passing data between tasks and co-routines and between ISR's and
|
||||
* co-routines.
|
||||
*
|
||||
* @param xQueue The handle to the queue on which the item is to be posted.
|
||||
*
|
||||
* @param pvBuffer A pointer to a buffer into which the received item will be
|
||||
* placed. The size of the items the queue will hold was defined when the
|
||||
* queue was created, so this many bytes will be copied from the queue into
|
||||
* pvBuffer.
|
||||
*
|
||||
* @param pxCoRoutineWoken A co-routine may be blocked waiting for space to become
|
||||
* available on the queue. If crQUEUE_RECEIVE_FROM_ISR causes such a
|
||||
* co-routine to unblock *pxCoRoutineWoken will get set to pdTRUE, otherwise
|
||||
* *pxCoRoutineWoken will remain unchanged.
|
||||
*
|
||||
* @return pdTRUE an item was successfully received from the queue, otherwise
|
||||
* pdFALSE.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* // A co-routine that posts a character to a queue then blocks for a fixed
|
||||
* // period. The character is incremented each time.
|
||||
* static void vSendingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||
* {
|
||||
* // cChar holds its value while this co-routine is blocked and must therefore
|
||||
* // be declared static.
|
||||
* static char cCharToTx = 'a';
|
||||
* BaseType_t xResult;
|
||||
*
|
||||
* // All co-routines must start with a call to crSTART().
|
||||
* crSTART( xHandle );
|
||||
*
|
||||
* for( ;; )
|
||||
* {
|
||||
* // Send the next character to the queue.
|
||||
* crQUEUE_SEND( xHandle, xCoRoutineQueue, &cCharToTx, NO_DELAY, &xResult );
|
||||
*
|
||||
* if( xResult == pdPASS )
|
||||
* {
|
||||
* // The character was successfully posted to the queue.
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* // Could not post the character to the queue.
|
||||
* }
|
||||
*
|
||||
* // Enable the UART Tx interrupt to cause an interrupt in this
|
||||
* // hypothetical UART. The interrupt will obtain the character
|
||||
* // from the queue and send it.
|
||||
* ENABLE_RX_INTERRUPT();
|
||||
*
|
||||
* // Increment to the next character then block for a fixed period.
|
||||
* // cCharToTx will maintain its value across the delay as it is
|
||||
* // declared static.
|
||||
* cCharToTx++;
|
||||
* if( cCharToTx > 'x' )
|
||||
* {
|
||||
* cCharToTx = 'a';
|
||||
* }
|
||||
* crDELAY( 100 );
|
||||
* }
|
||||
*
|
||||
* // All co-routines must end with a call to crEND().
|
||||
* crEND();
|
||||
* }
|
||||
*
|
||||
* // An ISR that uses a queue to receive characters to send on a UART.
|
||||
* void vUART_ISR( void )
|
||||
* {
|
||||
* char cCharToTx;
|
||||
* BaseType_t xCRWokenByPost = pdFALSE;
|
||||
*
|
||||
* while( UART_TX_REG_EMPTY() )
|
||||
* {
|
||||
* // Are there any characters in the queue waiting to be sent?
|
||||
* // xCRWokenByPost will automatically be set to pdTRUE if a co-routine
|
||||
* // is woken by the post - ensuring that only a single co-routine is
|
||||
* // woken no matter how many times we go around this loop.
|
||||
* if( crQUEUE_RECEIVE_FROM_ISR( pxQueue, &cCharToTx, &xCRWokenByPost ) )
|
||||
* {
|
||||
* SEND_CHARACTER( cCharToTx );
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup crQUEUE_RECEIVE_FROM_ISR crQUEUE_RECEIVE_FROM_ISR
|
||||
* \ingroup Tasks
|
||||
*/
|
||||
#define crQUEUE_RECEIVE_FROM_ISR( pxQueue, pvBuffer, pxCoRoutineWoken ) \
|
||||
xQueueCRReceiveFromISR( ( pxQueue ), ( pvBuffer ), ( pxCoRoutineWoken ) )
|
||||
|
||||
/*
|
||||
* This function is intended for internal use by the co-routine macros only.
|
||||
* The macro nature of the co-routine implementation requires that the
|
||||
* prototype appears here. The function should not be used by application
|
||||
* writers.
|
||||
*
|
||||
* Removes the current co-routine from its ready list and places it in the
|
||||
* appropriate delayed list.
|
||||
*/
|
||||
void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay,
|
||||
List_t * pxEventList );
|
||||
|
||||
/*
|
||||
* This function is intended for internal use by the queue implementation only.
|
||||
* The function should not be used by application writers.
|
||||
*
|
||||
* Removes the highest priority co-routine from the event list and places it in
|
||||
* the pending ready list.
|
||||
*/
|
||||
BaseType_t xCoRoutineRemoveFromEventList( const List_t * pxEventList );
|
||||
|
||||
|
||||
/*
|
||||
* This function resets the internal state of the coroutine module. It must be
|
||||
* called by the application before restarting the scheduler.
|
||||
*/
|
||||
void vCoRoutineResetState( void ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
#endif /* CO_ROUTINE_H */
|
||||
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DEPRECATED_DEFINITIONS_H
|
||||
#define DEPRECATED_DEFINITIONS_H
|
||||
|
||||
|
||||
/* Each FreeRTOS port has a unique portmacro.h header file. Originally a
|
||||
* pre-processor definition was used to ensure the pre-processor found the correct
|
||||
* portmacro.h file for the port being used. That scheme was deprecated in favour
|
||||
* of setting the compiler's include path such that it found the correct
|
||||
* portmacro.h file - removing the need for the constant and allowing the
|
||||
* portmacro.h file to be located anywhere in relation to the port being used. The
|
||||
* definitions below remain in the code for backward compatibility only. New
|
||||
* projects should not use them. */
|
||||
|
||||
#ifdef OPEN_WATCOM_INDUSTRIAL_PC_PORT
|
||||
#include "..\..\Source\portable\owatcom\16bitdos\pc\portmacro.h"
|
||||
typedef void ( __interrupt __far * pxISR )();
|
||||
#endif
|
||||
|
||||
#ifdef OPEN_WATCOM_FLASH_LITE_186_PORT
|
||||
#include "..\..\Source\portable\owatcom\16bitdos\flsh186\portmacro.h"
|
||||
typedef void ( __interrupt __far * pxISR )();
|
||||
#endif
|
||||
|
||||
#ifdef GCC_MEGA_AVR
|
||||
#include "../portable/GCC/ATMega323/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef IAR_MEGA_AVR
|
||||
#include "../portable/IAR/ATMega323/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef MPLAB_PIC24_PORT
|
||||
#include "../../Source/portable/MPLAB/PIC24_dsPIC/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef MPLAB_DSPIC_PORT
|
||||
#include "../../Source/portable/MPLAB/PIC24_dsPIC/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef MPLAB_PIC18F_PORT
|
||||
#include "../../Source/portable/MPLAB/PIC18F/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef MPLAB_PIC32MX_PORT
|
||||
#include "../../Source/portable/MPLAB/PIC32MX/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef _FEDPICC
|
||||
#include "libFreeRTOS/Include/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef SDCC_CYGNAL
|
||||
#include "../../Source/portable/SDCC/Cygnal/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef GCC_ARM7
|
||||
#include "../../Source/portable/GCC/ARM7_LPC2000/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef GCC_ARM7_ECLIPSE
|
||||
#include "portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef ROWLEY_LPC23xx
|
||||
#include "../../Source/portable/GCC/ARM7_LPC23xx/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef IAR_MSP430
|
||||
#include "..\..\Source\portable\IAR\MSP430\portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef GCC_MSP430
|
||||
#include "../../Source/portable/GCC/MSP430F449/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef ROWLEY_MSP430
|
||||
#include "../../Source/portable/Rowley/MSP430F449/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARM7_LPC21xx_KEIL_RVDS
|
||||
#include "..\..\Source\portable\RVDS\ARM7_LPC21xx\portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef SAM7_GCC
|
||||
#include "../../Source/portable/GCC/ARM7_AT91SAM7S/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef SAM7_IAR
|
||||
#include "..\..\Source\portable\IAR\AtmelSAM7S64\portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef SAM9XE_IAR
|
||||
#include "..\..\Source\portable\IAR\AtmelSAM9XE\portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef LPC2000_IAR
|
||||
#include "..\..\Source\portable\IAR\LPC2000\portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef STR71X_IAR
|
||||
#include "..\..\Source\portable\IAR\STR71x\portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef STR75X_IAR
|
||||
#include "..\..\Source\portable\IAR\STR75x\portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef STR75X_GCC
|
||||
#include "..\..\Source\portable\GCC\STR75x\portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef STR91X_IAR
|
||||
#include "..\..\Source\portable\IAR\STR91x\portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef GCC_H8S
|
||||
#include "../../Source/portable/GCC/H8S2329/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef GCC_AT91FR40008
|
||||
#include "../../Source/portable/GCC/ARM7_AT91FR40008/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef RVDS_ARMCM3_LM3S102
|
||||
#include "../../Source/portable/RVDS/ARM_CM3/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef GCC_ARMCM3_LM3S102
|
||||
#include "../../Source/portable/GCC/ARM_CM3/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef GCC_ARMCM3
|
||||
#include "../../Source/portable/GCC/ARM_CM3/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef IAR_ARM_CM3
|
||||
#include "../../Source/portable/IAR/ARM_CM3/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef IAR_ARMCM3_LM
|
||||
#include "../../Source/portable/IAR/ARM_CM3/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef HCS12_CODE_WARRIOR
|
||||
#include "../../Source/portable/CodeWarrior/HCS12/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef MICROBLAZE_GCC
|
||||
#include "../../Source/portable/GCC/MicroBlaze/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef TERN_EE
|
||||
#include "..\..\Source\portable\Paradigm\Tern_EE\small\portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef GCC_HCS12
|
||||
#include "../../Source/portable/GCC/HCS12/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef GCC_MCF5235
|
||||
#include "../../Source/portable/GCC/MCF5235/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef COLDFIRE_V2_GCC
|
||||
#include "../../../Source/portable/GCC/ColdFire_V2/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef COLDFIRE_V2_CODEWARRIOR
|
||||
#include "../../Source/portable/CodeWarrior/ColdFire_V2/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef GCC_PPC405
|
||||
#include "../../Source/portable/GCC/PPC405_Xilinx/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef GCC_PPC440
|
||||
#include "../../Source/portable/GCC/PPC440_Xilinx/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef _16FX_SOFTUNE
|
||||
#include "..\..\Source\portable\Softune\MB96340\portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef BCC_INDUSTRIAL_PC_PORT
|
||||
|
||||
/* A short file name has to be used in place of the normal
|
||||
* FreeRTOSConfig.h when using the Borland compiler. */
|
||||
#include "frconfig.h"
|
||||
#include "..\portable\BCC\16BitDOS\PC\prtmacro.h"
|
||||
typedef void ( __interrupt __far * pxISR )();
|
||||
#endif
|
||||
|
||||
#ifdef BCC_FLASH_LITE_186_PORT
|
||||
|
||||
/* A short file name has to be used in place of the normal
|
||||
* FreeRTOSConfig.h when using the Borland compiler. */
|
||||
#include "frconfig.h"
|
||||
#include "..\portable\BCC\16BitDOS\flsh186\prtmacro.h"
|
||||
typedef void ( __interrupt __far * pxISR )();
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#ifdef __AVR32_AVR32A__
|
||||
#include "portmacro.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __ICCAVR32__
|
||||
#ifdef __CORE__
|
||||
#if __CORE__ == __AVR32A__
|
||||
#include "portmacro.h"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __91467D
|
||||
#include "portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef __96340
|
||||
#include "portmacro.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __IAR_V850ES_Fx3__
|
||||
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef __IAR_V850ES_Jx3__
|
||||
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef __IAR_V850ES_Jx3_L__
|
||||
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef __IAR_V850ES_Jx2__
|
||||
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef __IAR_V850ES_Hx2__
|
||||
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef __IAR_78K0R_Kx3__
|
||||
#include "../../Source/portable/IAR/78K0R/portmacro.h"
|
||||
#endif
|
||||
|
||||
#ifdef __IAR_78K0R_Kx3L__
|
||||
#include "../../Source/portable/IAR/78K0R/portmacro.h"
|
||||
#endif
|
||||
|
||||
#endif /* DEPRECATED_DEFINITIONS_H */
|
||||
+848
@@ -0,0 +1,848 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef EVENT_GROUPS_H
|
||||
#define EVENT_GROUPS_H
|
||||
|
||||
#ifndef INC_FREERTOS_H
|
||||
#error "include FreeRTOS.h" must appear in source files before "include event_groups.h"
|
||||
#endif
|
||||
|
||||
/* FreeRTOS includes. */
|
||||
#include "timers.h"
|
||||
|
||||
/* The following bit fields convey control information in a task's event list
|
||||
* item value. It is important they don't clash with the
|
||||
* taskEVENT_LIST_ITEM_VALUE_IN_USE definition. */
|
||||
#if ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS )
|
||||
#define eventCLEAR_EVENTS_ON_EXIT_BIT ( ( uint16_t ) 0x0100U )
|
||||
#define eventUNBLOCKED_DUE_TO_BIT_SET ( ( uint16_t ) 0x0200U )
|
||||
#define eventWAIT_FOR_ALL_BITS ( ( uint16_t ) 0x0400U )
|
||||
#define eventEVENT_BITS_CONTROL_BYTES ( ( uint16_t ) 0xff00U )
|
||||
#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_32_BITS )
|
||||
#define eventCLEAR_EVENTS_ON_EXIT_BIT ( ( uint32_t ) 0x01000000U )
|
||||
#define eventUNBLOCKED_DUE_TO_BIT_SET ( ( uint32_t ) 0x02000000U )
|
||||
#define eventWAIT_FOR_ALL_BITS ( ( uint32_t ) 0x04000000U )
|
||||
#define eventEVENT_BITS_CONTROL_BYTES ( ( uint32_t ) 0xff000000U )
|
||||
#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_64_BITS )
|
||||
#define eventCLEAR_EVENTS_ON_EXIT_BIT ( ( uint64_t ) 0x0100000000000000U )
|
||||
#define eventUNBLOCKED_DUE_TO_BIT_SET ( ( uint64_t ) 0x0200000000000000U )
|
||||
#define eventWAIT_FOR_ALL_BITS ( ( uint64_t ) 0x0400000000000000U )
|
||||
#define eventEVENT_BITS_CONTROL_BYTES ( ( uint64_t ) 0xff00000000000000U )
|
||||
#endif /* if ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS ) */
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/**
|
||||
* An event group is a collection of bits to which an application can assign a
|
||||
* meaning. For example, an application may create an event group to convey
|
||||
* the status of various CAN bus related events in which bit 0 might mean "A CAN
|
||||
* message has been received and is ready for processing", bit 1 might mean "The
|
||||
* application has queued a message that is ready for sending onto the CAN
|
||||
* network", and bit 2 might mean "It is time to send a SYNC message onto the
|
||||
* CAN network" etc. A task can then test the bit values to see which events
|
||||
* are active, and optionally enter the Blocked state to wait for a specified
|
||||
* bit or a group of specified bits to be active. To continue the CAN bus
|
||||
* example, a CAN controlling task can enter the Blocked state (and therefore
|
||||
* not consume any processing time) until either bit 0, bit 1 or bit 2 are
|
||||
* active, at which time the bit that was actually active would inform the task
|
||||
* which action it had to take (process a received message, send a message, or
|
||||
* send a SYNC).
|
||||
*
|
||||
* The event groups implementation contains intelligence to avoid race
|
||||
* conditions that would otherwise occur were an application to use a simple
|
||||
* variable for the same purpose. This is particularly important with respect
|
||||
* to when a bit within an event group is to be cleared, and when bits have to
|
||||
* be set and then tested atomically - as is the case where event groups are
|
||||
* used to create a synchronisation point between multiple tasks (a
|
||||
* 'rendezvous').
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
*
|
||||
* Type by which event groups are referenced. For example, a call to
|
||||
* xEventGroupCreate() returns an EventGroupHandle_t variable that can then
|
||||
* be used as a parameter to other event group functions.
|
||||
*
|
||||
* \defgroup EventGroupHandle_t EventGroupHandle_t
|
||||
* \ingroup EventGroup
|
||||
*/
|
||||
struct EventGroupDef_t;
|
||||
typedef struct EventGroupDef_t * EventGroupHandle_t;
|
||||
|
||||
/*
|
||||
* The type that holds event bits always matches TickType_t - therefore the
|
||||
* number of bits it holds is set by configTICK_TYPE_WIDTH_IN_BITS (16 bits if set to 0,
|
||||
* 32 bits if set to 1, 64 bits if set to 2.
|
||||
*
|
||||
* \defgroup EventBits_t EventBits_t
|
||||
* \ingroup EventGroup
|
||||
*/
|
||||
typedef TickType_t EventBits_t;
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
* @code{c}
|
||||
* EventGroupHandle_t xEventGroupCreate( void );
|
||||
* @endcode
|
||||
*
|
||||
* Create a new event group.
|
||||
*
|
||||
* Internally, within the FreeRTOS implementation, event groups use a [small]
|
||||
* block of memory, in which the event group's structure is stored. If an event
|
||||
* groups is created using xEventGroupCreate() then the required memory is
|
||||
* automatically dynamically allocated inside the xEventGroupCreate() function.
|
||||
* (see https://www.FreeRTOS.org/a00111.html). If an event group is created
|
||||
* using xEventGroupCreateStatic() then the application writer must instead
|
||||
* provide the memory that will get used by the event group.
|
||||
* xEventGroupCreateStatic() therefore allows an event group to be created
|
||||
* without using any dynamic memory allocation.
|
||||
*
|
||||
* Although event groups are not related to ticks, for internal implementation
|
||||
* reasons the number of bits available for use in an event group is dependent
|
||||
* on the configTICK_TYPE_WIDTH_IN_BITS setting in FreeRTOSConfig.h. If
|
||||
* configTICK_TYPE_WIDTH_IN_BITS is 0 then each event group contains 8 usable bits (bit
|
||||
* 0 to bit 7). If configTICK_TYPE_WIDTH_IN_BITS is set to 1 then each event group has
|
||||
* 24 usable bits (bit 0 to bit 23). If configTICK_TYPE_WIDTH_IN_BITS is set to 2 then
|
||||
* each event group has 56 usable bits (bit 0 to bit 53). The EventBits_t type
|
||||
* is used to store event bits within an event group.
|
||||
*
|
||||
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupCreate()
|
||||
* to be available.
|
||||
*
|
||||
* @return If the event group was created then a handle to the event group is
|
||||
* returned. If there was insufficient FreeRTOS heap available to create the
|
||||
* event group then NULL is returned. See https://www.FreeRTOS.org/a00111.html
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* // Declare a variable to hold the created event group.
|
||||
* EventGroupHandle_t xCreatedEventGroup;
|
||||
*
|
||||
* // Attempt to create the event group.
|
||||
* xCreatedEventGroup = xEventGroupCreate();
|
||||
*
|
||||
* // Was the event group created successfully?
|
||||
* if( xCreatedEventGroup == NULL )
|
||||
* {
|
||||
* // The event group was not created because there was insufficient
|
||||
* // FreeRTOS heap available.
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* // The event group was created.
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup xEventGroupCreate xEventGroupCreate
|
||||
* \ingroup EventGroup
|
||||
*/
|
||||
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
|
||||
EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
* @code{c}
|
||||
* EventGroupHandle_t xEventGroupCreateStatic( EventGroupHandle_t * pxEventGroupBuffer );
|
||||
* @endcode
|
||||
*
|
||||
* Create a new event group.
|
||||
*
|
||||
* Internally, within the FreeRTOS implementation, event groups use a [small]
|
||||
* block of memory, in which the event group's structure is stored. If an event
|
||||
* groups is created using xEventGroupCreate() then the required memory is
|
||||
* automatically dynamically allocated inside the xEventGroupCreate() function.
|
||||
* (see https://www.FreeRTOS.org/a00111.html). If an event group is created
|
||||
* using xEventGroupCreateStatic() then the application writer must instead
|
||||
* provide the memory that will get used by the event group.
|
||||
* xEventGroupCreateStatic() therefore allows an event group to be created
|
||||
* without using any dynamic memory allocation.
|
||||
*
|
||||
* Although event groups are not related to ticks, for internal implementation
|
||||
* reasons the number of bits available for use in an event group is dependent
|
||||
* on the configTICK_TYPE_WIDTH_IN_BITS setting in FreeRTOSConfig.h. If
|
||||
* configTICK_TYPE_WIDTH_IN_BITS is 0 then each event group contains 8 usable bits (bit
|
||||
* 0 to bit 7). If configTICK_TYPE_WIDTH_IN_BITS is set to 1 then each event group has
|
||||
* 24 usable bits (bit 0 to bit 23). If configTICK_TYPE_WIDTH_IN_BITS is set to 2 then
|
||||
* each event group has 56 usable bits (bit 0 to bit 53). The EventBits_t type
|
||||
* is used to store event bits within an event group.
|
||||
*
|
||||
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupCreateStatic()
|
||||
* to be available.
|
||||
*
|
||||
* @param pxEventGroupBuffer pxEventGroupBuffer must point to a variable of type
|
||||
* StaticEventGroup_t, which will be then be used to hold the event group's data
|
||||
* structures, removing the need for the memory to be allocated dynamically.
|
||||
*
|
||||
* @return If the event group was created then a handle to the event group is
|
||||
* returned. If pxEventGroupBuffer was NULL then NULL is returned.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* // StaticEventGroup_t is a publicly accessible structure that has the same
|
||||
* // size and alignment requirements as the real event group structure. It is
|
||||
* // provided as a mechanism for applications to know the size of the event
|
||||
* // group (which is dependent on the architecture and configuration file
|
||||
* // settings) without breaking the strict data hiding policy by exposing the
|
||||
* // real event group internals. This StaticEventGroup_t variable is passed
|
||||
* // into the xSemaphoreCreateEventGroupStatic() function and is used to store
|
||||
* // the event group's data structures
|
||||
* StaticEventGroup_t xEventGroupBuffer;
|
||||
*
|
||||
* // Create the event group without dynamically allocating any memory.
|
||||
* xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
|
||||
* @endcode
|
||||
*/
|
||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer ) PRIVILEGED_FUNCTION;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
* @code{c}
|
||||
* EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
|
||||
* const EventBits_t uxBitsToWaitFor,
|
||||
* const BaseType_t xClearOnExit,
|
||||
* const BaseType_t xWaitForAllBits,
|
||||
* const TickType_t xTicksToWait );
|
||||
* @endcode
|
||||
*
|
||||
* [Potentially] block to wait for one or more bits to be set within a
|
||||
* previously created event group.
|
||||
*
|
||||
* This function cannot be called from an interrupt.
|
||||
*
|
||||
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupWaitBits()
|
||||
* to be available.
|
||||
*
|
||||
* @param xEventGroup The event group in which the bits are being tested. The
|
||||
* event group must have previously been created using a call to
|
||||
* xEventGroupCreate().
|
||||
*
|
||||
* @param uxBitsToWaitFor A bitwise value that indicates the bit or bits to test
|
||||
* inside the event group. For example, to wait for bit 0 and/or bit 2 set
|
||||
* uxBitsToWaitFor to 0x05. To wait for bits 0 and/or bit 1 and/or bit 2 set
|
||||
* uxBitsToWaitFor to 0x07. Etc.
|
||||
*
|
||||
* @param xClearOnExit If xClearOnExit is set to pdTRUE then any bits within
|
||||
* uxBitsToWaitFor that are set within the event group will be cleared before
|
||||
* xEventGroupWaitBits() returns if the wait condition was met (if the function
|
||||
* returns for a reason other than a timeout). If xClearOnExit is set to
|
||||
* pdFALSE then the bits set in the event group are not altered when the call to
|
||||
* xEventGroupWaitBits() returns.
|
||||
*
|
||||
* @param xWaitForAllBits If xWaitForAllBits is set to pdTRUE then
|
||||
* xEventGroupWaitBits() will return when either all the bits in uxBitsToWaitFor
|
||||
* are set or the specified block time expires. If xWaitForAllBits is set to
|
||||
* pdFALSE then xEventGroupWaitBits() will return when any one of the bits set
|
||||
* in uxBitsToWaitFor is set or the specified block time expires. The block
|
||||
* time is specified by the xTicksToWait parameter.
|
||||
*
|
||||
* @param xTicksToWait The maximum amount of time (specified in 'ticks') to wait
|
||||
* for one/all (depending on the xWaitForAllBits value) of the bits specified by
|
||||
* uxBitsToWaitFor to become set. A value of portMAX_DELAY can be used to block
|
||||
* indefinitely (provided INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h).
|
||||
*
|
||||
* @return The value of the event group at the time either the bits being waited
|
||||
* for became set, or the block time expired. Test the return value to know
|
||||
* which bits were set. If xEventGroupWaitBits() returned because its timeout
|
||||
* expired then not all the bits being waited for will be set. If
|
||||
* xEventGroupWaitBits() returned because the bits it was waiting for were set
|
||||
* then the returned value is the event group value before any bits were
|
||||
* automatically cleared in the case that xClearOnExit parameter was set to
|
||||
* pdTRUE.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* #define BIT_0 ( 1 << 0 )
|
||||
* #define BIT_4 ( 1 << 4 )
|
||||
*
|
||||
* void aFunction( EventGroupHandle_t xEventGroup )
|
||||
* {
|
||||
* EventBits_t uxBits;
|
||||
* const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
|
||||
*
|
||||
* // Wait a maximum of 100ms for either bit 0 or bit 4 to be set within
|
||||
* // the event group. Clear the bits before exiting.
|
||||
* uxBits = xEventGroupWaitBits(
|
||||
* xEventGroup, // The event group being tested.
|
||||
* BIT_0 | BIT_4, // The bits within the event group to wait for.
|
||||
* pdTRUE, // BIT_0 and BIT_4 should be cleared before returning.
|
||||
* pdFALSE, // Don't wait for both bits, either bit will do.
|
||||
* xTicksToWait ); // Wait a maximum of 100ms for either bit to be set.
|
||||
*
|
||||
* if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
|
||||
* {
|
||||
* // xEventGroupWaitBits() returned because both bits were set.
|
||||
* }
|
||||
* else if( ( uxBits & BIT_0 ) != 0 )
|
||||
* {
|
||||
* // xEventGroupWaitBits() returned because just BIT_0 was set.
|
||||
* }
|
||||
* else if( ( uxBits & BIT_4 ) != 0 )
|
||||
* {
|
||||
* // xEventGroupWaitBits() returned because just BIT_4 was set.
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* // xEventGroupWaitBits() returned because xTicksToWait ticks passed
|
||||
* // without either BIT_0 or BIT_4 becoming set.
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup xEventGroupWaitBits xEventGroupWaitBits
|
||||
* \ingroup EventGroup
|
||||
*/
|
||||
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToWaitFor,
|
||||
const BaseType_t xClearOnExit,
|
||||
const BaseType_t xWaitForAllBits,
|
||||
TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
* @code{c}
|
||||
* EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
|
||||
* @endcode
|
||||
*
|
||||
* Clear bits within an event group. This function cannot be called from an
|
||||
* interrupt.
|
||||
*
|
||||
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupClearBits()
|
||||
* to be available.
|
||||
*
|
||||
* @param xEventGroup The event group in which the bits are to be cleared.
|
||||
*
|
||||
* @param uxBitsToClear A bitwise value that indicates the bit or bits to clear
|
||||
* in the event group. For example, to clear bit 3 only, set uxBitsToClear to
|
||||
* 0x08. To clear bit 3 and bit 0 set uxBitsToClear to 0x09.
|
||||
*
|
||||
* @return The value of the event group before the specified bits were cleared.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* #define BIT_0 ( 1 << 0 )
|
||||
* #define BIT_4 ( 1 << 4 )
|
||||
*
|
||||
* void aFunction( EventGroupHandle_t xEventGroup )
|
||||
* {
|
||||
* EventBits_t uxBits;
|
||||
*
|
||||
* // Clear bit 0 and bit 4 in xEventGroup.
|
||||
* uxBits = xEventGroupClearBits(
|
||||
* xEventGroup, // The event group being updated.
|
||||
* BIT_0 | BIT_4 );// The bits being cleared.
|
||||
*
|
||||
* if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
|
||||
* {
|
||||
* // Both bit 0 and bit 4 were set before xEventGroupClearBits() was
|
||||
* // called. Both will now be clear (not set).
|
||||
* }
|
||||
* else if( ( uxBits & BIT_0 ) != 0 )
|
||||
* {
|
||||
* // Bit 0 was set before xEventGroupClearBits() was called. It will
|
||||
* // now be clear.
|
||||
* }
|
||||
* else if( ( uxBits & BIT_4 ) != 0 )
|
||||
* {
|
||||
* // Bit 4 was set before xEventGroupClearBits() was called. It will
|
||||
* // now be clear.
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* // Neither bit 0 nor bit 4 were set in the first place.
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup xEventGroupClearBits xEventGroupClearBits
|
||||
* \ingroup EventGroup
|
||||
*/
|
||||
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
* @code{c}
|
||||
* BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
|
||||
* @endcode
|
||||
*
|
||||
* A version of xEventGroupClearBits() that can be called from an interrupt.
|
||||
*
|
||||
* Setting bits in an event group is not a deterministic operation because there
|
||||
* are an unknown number of tasks that may be waiting for the bit or bits being
|
||||
* set. FreeRTOS does not allow nondeterministic operations to be performed
|
||||
* while interrupts are disabled, so protects event groups that are accessed
|
||||
* from tasks by suspending the scheduler rather than disabling interrupts. As
|
||||
* a result event groups cannot be accessed directly from an interrupt service
|
||||
* routine. Therefore xEventGroupClearBitsFromISR() sends a message to the
|
||||
* timer task to have the clear operation performed in the context of the timer
|
||||
* task.
|
||||
*
|
||||
* @note If this function returns pdPASS then the timer task is ready to run
|
||||
* and a portYIELD_FROM_ISR(pdTRUE) should be executed to perform the needed
|
||||
* clear on the event group. This behavior is different from
|
||||
* xEventGroupSetBitsFromISR because the parameter xHigherPriorityTaskWoken is
|
||||
* not present.
|
||||
*
|
||||
* @param xEventGroup The event group in which the bits are to be cleared.
|
||||
*
|
||||
* @param uxBitsToClear A bitwise value that indicates the bit or bits to clear.
|
||||
* For example, to clear bit 3 only, set uxBitsToClear to 0x08. To clear bit 3
|
||||
* and bit 0 set uxBitsToClear to 0x09.
|
||||
*
|
||||
* @return If the request to execute the function was posted successfully then
|
||||
* pdPASS is returned, otherwise pdFALSE is returned. pdFALSE will be returned
|
||||
* if the timer service queue was full.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* #define BIT_0 ( 1 << 0 )
|
||||
* #define BIT_4 ( 1 << 4 )
|
||||
*
|
||||
* // An event group which it is assumed has already been created by a call to
|
||||
* // xEventGroupCreate().
|
||||
* EventGroupHandle_t xEventGroup;
|
||||
*
|
||||
* void anInterruptHandler( void )
|
||||
* {
|
||||
* // Clear bit 0 and bit 4 in xEventGroup.
|
||||
* xResult = xEventGroupClearBitsFromISR(
|
||||
* xEventGroup, // The event group being updated.
|
||||
* BIT_0 | BIT_4 ); // The bits being set.
|
||||
*
|
||||
* if( xResult == pdPASS )
|
||||
* {
|
||||
* // The message was posted successfully.
|
||||
* portYIELD_FROM_ISR(pdTRUE);
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup xEventGroupClearBitsFromISR xEventGroupClearBitsFromISR
|
||||
* \ingroup EventGroup
|
||||
*/
|
||||
#if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) )
|
||||
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION;
|
||||
#endif /* if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) */
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
* @code{c}
|
||||
* EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
|
||||
* @endcode
|
||||
*
|
||||
* Set bits within an event group.
|
||||
* This function cannot be called from an interrupt. xEventGroupSetBitsFromISR()
|
||||
* is a version that can be called from an interrupt.
|
||||
*
|
||||
* Setting bits in an event group will automatically unblock tasks that are
|
||||
* blocked waiting for the bits.
|
||||
*
|
||||
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupSetBits()
|
||||
* to be available.
|
||||
*
|
||||
* @param xEventGroup The event group in which the bits are to be set.
|
||||
*
|
||||
* @param uxBitsToSet A bitwise value that indicates the bit or bits to set.
|
||||
* For example, to set bit 3 only, set uxBitsToSet to 0x08. To set bit 3
|
||||
* and bit 0 set uxBitsToSet to 0x09.
|
||||
*
|
||||
* @return The value of the event group at the time the call to
|
||||
* xEventGroupSetBits() returns. Returned value might have the bits specified
|
||||
* by the uxBitsToSet parameter cleared if setting a bit results in a task
|
||||
* that was waiting for the bit leaving the blocked state then it is possible
|
||||
* the bit will be cleared automatically (see the xClearBitOnExit parameter
|
||||
* of xEventGroupWaitBits()).
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* #define BIT_0 ( 1 << 0 )
|
||||
* #define BIT_4 ( 1 << 4 )
|
||||
*
|
||||
* void aFunction( EventGroupHandle_t xEventGroup )
|
||||
* {
|
||||
* EventBits_t uxBits;
|
||||
*
|
||||
* // Set bit 0 and bit 4 in xEventGroup.
|
||||
* uxBits = xEventGroupSetBits(
|
||||
* xEventGroup, // The event group being updated.
|
||||
* BIT_0 | BIT_4 );// The bits being set.
|
||||
*
|
||||
* if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
|
||||
* {
|
||||
* // Both bit 0 and bit 4 remained set when the function returned.
|
||||
* }
|
||||
* else if( ( uxBits & BIT_0 ) != 0 )
|
||||
* {
|
||||
* // Bit 0 remained set when the function returned, but bit 4 was
|
||||
* // cleared. It might be that bit 4 was cleared automatically as a
|
||||
* // task that was waiting for bit 4 was removed from the Blocked
|
||||
* // state.
|
||||
* }
|
||||
* else if( ( uxBits & BIT_4 ) != 0 )
|
||||
* {
|
||||
* // Bit 4 remained set when the function returned, but bit 0 was
|
||||
* // cleared. It might be that bit 0 was cleared automatically as a
|
||||
* // task that was waiting for bit 0 was removed from the Blocked
|
||||
* // state.
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* // Neither bit 0 nor bit 4 remained set. It might be that a task
|
||||
* // was waiting for both of the bits to be set, and the bits were
|
||||
* // cleared as the task left the Blocked state.
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup xEventGroupSetBits xEventGroupSetBits
|
||||
* \ingroup EventGroup
|
||||
*/
|
||||
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
* @code{c}
|
||||
* BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken );
|
||||
* @endcode
|
||||
*
|
||||
* A version of xEventGroupSetBits() that can be called from an interrupt.
|
||||
*
|
||||
* Setting bits in an event group is not a deterministic operation because there
|
||||
* are an unknown number of tasks that may be waiting for the bit or bits being
|
||||
* set. FreeRTOS does not allow nondeterministic operations to be performed in
|
||||
* interrupts or from critical sections. Therefore xEventGroupSetBitsFromISR()
|
||||
* sends a message to the timer task to have the set operation performed in the
|
||||
* context of the timer task - where a scheduler lock is used in place of a
|
||||
* critical section.
|
||||
*
|
||||
* @param xEventGroup The event group in which the bits are to be set.
|
||||
*
|
||||
* @param uxBitsToSet A bitwise value that indicates the bit or bits to set.
|
||||
* For example, to set bit 3 only, set uxBitsToSet to 0x08. To set bit 3
|
||||
* and bit 0 set uxBitsToSet to 0x09.
|
||||
*
|
||||
* @param pxHigherPriorityTaskWoken As mentioned above, calling this function
|
||||
* will result in a message being sent to the timer daemon task. If the
|
||||
* priority of the timer daemon task is higher than the priority of the
|
||||
* currently running task (the task the interrupt interrupted) then
|
||||
* *pxHigherPriorityTaskWoken will be set to pdTRUE by
|
||||
* xEventGroupSetBitsFromISR(), indicating that a context switch should be
|
||||
* requested before the interrupt exits. For that reason
|
||||
* *pxHigherPriorityTaskWoken must be initialised to pdFALSE. See the
|
||||
* example code below.
|
||||
*
|
||||
* @return If the request to execute the function was posted successfully then
|
||||
* pdPASS is returned, otherwise pdFALSE is returned. pdFALSE will be returned
|
||||
* if the timer service queue was full.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* #define BIT_0 ( 1 << 0 )
|
||||
* #define BIT_4 ( 1 << 4 )
|
||||
*
|
||||
* // An event group which it is assumed has already been created by a call to
|
||||
* // xEventGroupCreate().
|
||||
* EventGroupHandle_t xEventGroup;
|
||||
*
|
||||
* void anInterruptHandler( void )
|
||||
* {
|
||||
* BaseType_t xHigherPriorityTaskWoken, xResult;
|
||||
*
|
||||
* // xHigherPriorityTaskWoken must be initialised to pdFALSE.
|
||||
* xHigherPriorityTaskWoken = pdFALSE;
|
||||
*
|
||||
* // Set bit 0 and bit 4 in xEventGroup.
|
||||
* xResult = xEventGroupSetBitsFromISR(
|
||||
* xEventGroup, // The event group being updated.
|
||||
* BIT_0 | BIT_4 // The bits being set.
|
||||
* &xHigherPriorityTaskWoken );
|
||||
*
|
||||
* // Was the message posted successfully?
|
||||
* if( xResult == pdPASS )
|
||||
* {
|
||||
* // If xHigherPriorityTaskWoken is now set to pdTRUE then a context
|
||||
* // switch should be requested. The macro used is port specific and
|
||||
* // will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() -
|
||||
* // refer to the documentation page for the port being used.
|
||||
* portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR
|
||||
* \ingroup EventGroup
|
||||
*/
|
||||
#if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) )
|
||||
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToSet,
|
||||
BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||
#endif /* if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) */
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
* @code{c}
|
||||
* EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
|
||||
* const EventBits_t uxBitsToSet,
|
||||
* const EventBits_t uxBitsToWaitFor,
|
||||
* TickType_t xTicksToWait );
|
||||
* @endcode
|
||||
*
|
||||
* Atomically set bits within an event group, then wait for a combination of
|
||||
* bits to be set within the same event group. This functionality is typically
|
||||
* used to synchronise multiple tasks, where each task has to wait for the other
|
||||
* tasks to reach a synchronisation point before proceeding.
|
||||
*
|
||||
* This function cannot be used from an interrupt.
|
||||
*
|
||||
* The function will return before its block time expires if the bits specified
|
||||
* by the uxBitsToWait parameter are set, or become set within that time. In
|
||||
* this case all the bits specified by uxBitsToWait will be automatically
|
||||
* cleared before the function returns.
|
||||
*
|
||||
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupSync()
|
||||
* to be available.
|
||||
*
|
||||
* @param xEventGroup The event group in which the bits are being tested. The
|
||||
* event group must have previously been created using a call to
|
||||
* xEventGroupCreate().
|
||||
*
|
||||
* @param uxBitsToSet The bits to set in the event group before determining
|
||||
* if, and possibly waiting for, all the bits specified by the uxBitsToWait
|
||||
* parameter are set.
|
||||
*
|
||||
* @param uxBitsToWaitFor A bitwise value that indicates the bit or bits to test
|
||||
* inside the event group. For example, to wait for bit 0 and bit 2 set
|
||||
* uxBitsToWaitFor to 0x05. To wait for bits 0 and bit 1 and bit 2 set
|
||||
* uxBitsToWaitFor to 0x07. Etc.
|
||||
*
|
||||
* @param xTicksToWait The maximum amount of time (specified in 'ticks') to wait
|
||||
* for all of the bits specified by uxBitsToWaitFor to become set.
|
||||
*
|
||||
* @return The value of the event group at the time either the bits being waited
|
||||
* for became set, or the block time expired. Test the return value to know
|
||||
* which bits were set. If xEventGroupSync() returned because its timeout
|
||||
* expired then not all the bits being waited for will be set. If
|
||||
* xEventGroupSync() returned because all the bits it was waiting for were
|
||||
* set then the returned value is the event group value before any bits were
|
||||
* automatically cleared.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
* // Bits used by the three tasks.
|
||||
* #define TASK_0_BIT ( 1 << 0 )
|
||||
* #define TASK_1_BIT ( 1 << 1 )
|
||||
* #define TASK_2_BIT ( 1 << 2 )
|
||||
*
|
||||
* #define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT )
|
||||
*
|
||||
* // Use an event group to synchronise three tasks. It is assumed this event
|
||||
* // group has already been created elsewhere.
|
||||
* EventGroupHandle_t xEventBits;
|
||||
*
|
||||
* void vTask0( void *pvParameters )
|
||||
* {
|
||||
* EventBits_t uxReturn;
|
||||
* TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
|
||||
*
|
||||
* for( ;; )
|
||||
* {
|
||||
* // Perform task functionality here.
|
||||
*
|
||||
* // Set bit 0 in the event flag to note this task has reached the
|
||||
* // sync point. The other two tasks will set the other two bits defined
|
||||
* // by ALL_SYNC_BITS. All three tasks have reached the synchronisation
|
||||
* // point when all the ALL_SYNC_BITS are set. Wait a maximum of 100ms
|
||||
* // for this to happen.
|
||||
* uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait );
|
||||
*
|
||||
* if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS )
|
||||
* {
|
||||
* // All three tasks reached the synchronisation point before the call
|
||||
* // to xEventGroupSync() timed out.
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* void vTask1( void *pvParameters )
|
||||
* {
|
||||
* for( ;; )
|
||||
* {
|
||||
* // Perform task functionality here.
|
||||
*
|
||||
* // Set bit 1 in the event flag to note this task has reached the
|
||||
* // synchronisation point. The other two tasks will set the other two
|
||||
* // bits defined by ALL_SYNC_BITS. All three tasks have reached the
|
||||
* // synchronisation point when all the ALL_SYNC_BITS are set. Wait
|
||||
* // indefinitely for this to happen.
|
||||
* xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
|
||||
*
|
||||
* // xEventGroupSync() was called with an indefinite block time, so
|
||||
* // this task will only reach here if the synchronisation was made by all
|
||||
* // three tasks, so there is no need to test the return value.
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* void vTask2( void *pvParameters )
|
||||
* {
|
||||
* for( ;; )
|
||||
* {
|
||||
* // Perform task functionality here.
|
||||
*
|
||||
* // Set bit 2 in the event flag to note this task has reached the
|
||||
* // synchronisation point. The other two tasks will set the other two
|
||||
* // bits defined by ALL_SYNC_BITS. All three tasks have reached the
|
||||
* // synchronisation point when all the ALL_SYNC_BITS are set. Wait
|
||||
* // indefinitely for this to happen.
|
||||
* xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
|
||||
*
|
||||
* // xEventGroupSync() was called with an indefinite block time, so
|
||||
* // this task will only reach here if the synchronisation was made by all
|
||||
* // three tasks, so there is no need to test the return value.
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @endcode
|
||||
* \defgroup xEventGroupSync xEventGroupSync
|
||||
* \ingroup EventGroup
|
||||
*/
|
||||
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToSet,
|
||||
const EventBits_t uxBitsToWaitFor,
|
||||
TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
|
||||
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
* @code{c}
|
||||
* EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
|
||||
* @endcode
|
||||
*
|
||||
* Returns the current value of the bits in an event group. This function
|
||||
* cannot be used from an interrupt.
|
||||
*
|
||||
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupGetBits()
|
||||
* to be available.
|
||||
*
|
||||
* @param xEventGroup The event group being queried.
|
||||
*
|
||||
* @return The event group bits at the time xEventGroupGetBits() was called.
|
||||
*
|
||||
* \defgroup xEventGroupGetBits xEventGroupGetBits
|
||||
* \ingroup EventGroup
|
||||
*/
|
||||
#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( ( xEventGroup ), 0 )
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
* @code{c}
|
||||
* EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
|
||||
* @endcode
|
||||
*
|
||||
* A version of xEventGroupGetBits() that can be called from an ISR.
|
||||
*
|
||||
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupGetBitsFromISR()
|
||||
* to be available.
|
||||
*
|
||||
* @param xEventGroup The event group being queried.
|
||||
*
|
||||
* @return The event group bits at the time xEventGroupGetBitsFromISR() was called.
|
||||
*
|
||||
* \defgroup xEventGroupGetBitsFromISR xEventGroupGetBitsFromISR
|
||||
* \ingroup EventGroup
|
||||
*/
|
||||
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
* @code{c}
|
||||
* void xEventGroupDelete( EventGroupHandle_t xEventGroup );
|
||||
* @endcode
|
||||
*
|
||||
* Delete an event group that was previously created by a call to
|
||||
* xEventGroupCreate(). Tasks that are blocked on the event group will be
|
||||
* unblocked and obtain 0 as the event group's value.
|
||||
*
|
||||
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for vEventGroupDelete()
|
||||
* to be available.
|
||||
*
|
||||
* @param xEventGroup The event group being deleted.
|
||||
*/
|
||||
void vEventGroupDelete( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/**
|
||||
* event_groups.h
|
||||
* @code{c}
|
||||
* BaseType_t xEventGroupGetStaticBuffer( EventGroupHandle_t xEventGroup,
|
||||
* StaticEventGroup_t ** ppxEventGroupBuffer );
|
||||
* @endcode
|
||||
*
|
||||
* Retrieve a pointer to a statically created event groups's data structure
|
||||
* buffer. It is the same buffer that is supplied at the time of creation.
|
||||
*
|
||||
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupGetStaticBuffer()
|
||||
* to be available.
|
||||
*
|
||||
* @param xEventGroup The event group for which to retrieve the buffer.
|
||||
*
|
||||
* @param ppxEventGroupBuffer Used to return a pointer to the event groups's
|
||||
* data structure buffer.
|
||||
*
|
||||
* @return pdTRUE if the buffer was retrieved, pdFALSE otherwise.
|
||||
*/
|
||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||
BaseType_t xEventGroupGetStaticBuffer( EventGroupHandle_t xEventGroup,
|
||||
StaticEventGroup_t ** ppxEventGroupBuffer ) PRIVILEGED_FUNCTION;
|
||||
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||||
|
||||
/* For internal use only. */
|
||||
void vEventGroupSetBitsCallback( void * pvEventGroup,
|
||||
uint32_t ulBitsToSet ) PRIVILEGED_FUNCTION;
|
||||
void vEventGroupClearBitsCallback( void * pvEventGroup,
|
||||
uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION;
|
||||
|
||||
|
||||
#if ( configUSE_TRACE_FACILITY == 1 )
|
||||
UBaseType_t uxEventGroupGetNumber( void * xEventGroup ) PRIVILEGED_FUNCTION;
|
||||
void vEventGroupSetNumber( void * xEventGroup,
|
||||
UBaseType_t uxEventGroupNumber ) PRIVILEGED_FUNCTION;
|
||||
#endif
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
#endif /* EVENT_GROUPS_H */
|
||||
+511
@@ -0,0 +1,511 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the list implementation used by the scheduler. While it is tailored
|
||||
* heavily for the schedulers needs, it is also available for use by
|
||||
* application code.
|
||||
*
|
||||
* list_ts can only store pointers to list_item_ts. Each ListItem_t contains a
|
||||
* numeric value (xItemValue). Most of the time the lists are sorted in
|
||||
* ascending item value order.
|
||||
*
|
||||
* Lists are created already containing one list item. The value of this
|
||||
* item is the maximum possible that can be stored, it is therefore always at
|
||||
* the end of the list and acts as a marker. The list member pxHead always
|
||||
* points to this marker - even though it is at the tail of the list. This
|
||||
* is because the tail contains a wrap back pointer to the true head of
|
||||
* the list.
|
||||
*
|
||||
* In addition to it's value, each list item contains a pointer to the next
|
||||
* item in the list (pxNext), a pointer to the list it is in (pxContainer)
|
||||
* and a pointer back to the object that contains it. These later two
|
||||
* pointers are included for efficiency of list manipulation. There is
|
||||
* effectively a two way link between the object containing the list item and
|
||||
* the list item itself.
|
||||
*
|
||||
*
|
||||
* \page ListIntroduction List Implementation
|
||||
* \ingroup FreeRTOSIntro
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LIST_H
|
||||
#define LIST_H
|
||||
|
||||
#ifndef INC_FREERTOS_H
|
||||
#error "FreeRTOS.h must be included before list.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The list structure members are modified from within interrupts, and therefore
|
||||
* by rights should be declared volatile. However, they are only modified in a
|
||||
* functionally atomic way (within critical sections of with the scheduler
|
||||
* suspended) and are either passed by reference into a function or indexed via
|
||||
* a volatile variable. Therefore, in all use cases tested so far, the volatile
|
||||
* qualifier can be omitted in order to provide a moderate performance
|
||||
* improvement without adversely affecting functional behaviour. The assembly
|
||||
* instructions generated by the IAR, ARM and GCC compilers when the respective
|
||||
* compiler's options were set for maximum optimisation has been inspected and
|
||||
* deemed to be as intended. That said, as compiler technology advances, and
|
||||
* especially if aggressive cross module optimisation is used (a use case that
|
||||
* has not been exercised to any great extend) then it is feasible that the
|
||||
* volatile qualifier will be needed for correct optimisation. It is expected
|
||||
* that a compiler removing essential code because, without the volatile
|
||||
* qualifier on the list structure members and with aggressive cross module
|
||||
* optimisation, the compiler deemed the code unnecessary will result in
|
||||
* complete and obvious failure of the scheduler. If this is ever experienced
|
||||
* then the volatile qualifier can be inserted in the relevant places within the
|
||||
* list structures by simply defining configLIST_VOLATILE to volatile in
|
||||
* FreeRTOSConfig.h (as per the example at the bottom of this comment block).
|
||||
* If configLIST_VOLATILE is not defined then the preprocessor directives below
|
||||
* will simply #define configLIST_VOLATILE away completely.
|
||||
*
|
||||
* To use volatile list structure members then add the following line to
|
||||
* FreeRTOSConfig.h (without the quotes):
|
||||
* "#define configLIST_VOLATILE volatile"
|
||||
*/
|
||||
#ifndef configLIST_VOLATILE
|
||||
#define configLIST_VOLATILE
|
||||
#endif /* configSUPPORT_CROSS_MODULE_OPTIMISATION */
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/* Macros that can be used to place known values within the list structures,
|
||||
* then check that the known values do not get corrupted during the execution of
|
||||
* the application. These may catch the list data structures being overwritten in
|
||||
* memory. They will not catch data errors caused by incorrect configuration or
|
||||
* use of FreeRTOS.*/
|
||||
#if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0 )
|
||||
/* Define the macros to do nothing. */
|
||||
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
|
||||
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
|
||||
#define listFIRST_LIST_INTEGRITY_CHECK_VALUE
|
||||
#define listSECOND_LIST_INTEGRITY_CHECK_VALUE
|
||||
#define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem )
|
||||
#define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem )
|
||||
#define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList )
|
||||
#define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList )
|
||||
#define listTEST_LIST_ITEM_INTEGRITY( pxItem )
|
||||
#define listTEST_LIST_INTEGRITY( pxList )
|
||||
#else /* if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0 ) */
|
||||
/* Define macros that add new members into the list structures. */
|
||||
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue1;
|
||||
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue2;
|
||||
#define listFIRST_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue1;
|
||||
#define listSECOND_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue2;
|
||||
|
||||
/* Define macros that set the new structure members to known values. */
|
||||
#define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue1 = pdINTEGRITY_CHECK_VALUE
|
||||
#define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue2 = pdINTEGRITY_CHECK_VALUE
|
||||
#define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ) ( pxList )->xListIntegrityValue1 = pdINTEGRITY_CHECK_VALUE
|
||||
#define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ) ( pxList )->xListIntegrityValue2 = pdINTEGRITY_CHECK_VALUE
|
||||
|
||||
/* Define macros that will assert if one of the structure members does not
|
||||
* contain its expected value. */
|
||||
#define listTEST_LIST_ITEM_INTEGRITY( pxItem ) configASSERT( ( ( pxItem )->xListItemIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxItem )->xListItemIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
|
||||
#define listTEST_LIST_INTEGRITY( pxList ) configASSERT( ( ( pxList )->xListIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxList )->xListIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
|
||||
#endif /* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES */
|
||||
|
||||
|
||||
/*
|
||||
* Definition of the only type of object that a list can contain.
|
||||
*/
|
||||
struct xLIST;
|
||||
struct xLIST_ITEM
|
||||
{
|
||||
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /**< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||
configLIST_VOLATILE TickType_t xItemValue; /**< The value being listed. In most cases this is used to sort the list in ascending order. */
|
||||
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /**< Pointer to the next ListItem_t in the list. */
|
||||
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /**< Pointer to the previous ListItem_t in the list. */
|
||||
void * pvOwner; /**< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
|
||||
struct xLIST * configLIST_VOLATILE pxContainer; /**< Pointer to the list in which this list item is placed (if any). */
|
||||
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /**< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||
};
|
||||
typedef struct xLIST_ITEM ListItem_t;
|
||||
|
||||
#if ( configUSE_MINI_LIST_ITEM == 1 )
|
||||
struct xMINI_LIST_ITEM
|
||||
{
|
||||
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /**< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||
configLIST_VOLATILE TickType_t xItemValue;
|
||||
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
|
||||
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
|
||||
};
|
||||
typedef struct xMINI_LIST_ITEM MiniListItem_t;
|
||||
#else
|
||||
typedef struct xLIST_ITEM MiniListItem_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Definition of the type of queue used by the scheduler.
|
||||
*/
|
||||
typedef struct xLIST
|
||||
{
|
||||
listFIRST_LIST_INTEGRITY_CHECK_VALUE /**< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
|
||||
ListItem_t * configLIST_VOLATILE pxIndex; /**< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
|
||||
MiniListItem_t xListEnd; /**< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
|
||||
listSECOND_LIST_INTEGRITY_CHECK_VALUE /**< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||
} List_t;
|
||||
|
||||
/*
|
||||
* Access macro to set the owner of a list item. The owner of a list item
|
||||
* is the object (usually a TCB) that contains the list item.
|
||||
*
|
||||
* \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
|
||||
|
||||
/*
|
||||
* Access macro to get the owner of a list item. The owner of a list item
|
||||
* is the object (usually a TCB) that contains the list item.
|
||||
*
|
||||
* \page listGET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner )
|
||||
|
||||
/*
|
||||
* Access macro to set the value of the list item. In most cases the value is
|
||||
* used to sort the list in ascending order.
|
||||
*
|
||||
* \page listSET_LIST_ITEM_VALUE listSET_LIST_ITEM_VALUE
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) )
|
||||
|
||||
/*
|
||||
* Access macro to retrieve the value of the list item. The value can
|
||||
* represent anything - for example the priority of a task, or the time at
|
||||
* which a task should be unblocked.
|
||||
*
|
||||
* \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue )
|
||||
|
||||
/*
|
||||
* Access macro to retrieve the value of the list item at the head of a given
|
||||
* list.
|
||||
*
|
||||
* \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )
|
||||
|
||||
/*
|
||||
* Return the list item at the head of the list.
|
||||
*
|
||||
* \page listGET_HEAD_ENTRY listGET_HEAD_ENTRY
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext )
|
||||
|
||||
/*
|
||||
* Return the next list item.
|
||||
*
|
||||
* \page listGET_NEXT listGET_NEXT
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext )
|
||||
|
||||
/*
|
||||
* Return the list item that marks the end of the list
|
||||
*
|
||||
* \page listGET_END_MARKER listGET_END_MARKER
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )
|
||||
|
||||
/*
|
||||
* Access macro to determine if a list contains any items. The macro will
|
||||
* only have the value true if the list is empty.
|
||||
*
|
||||
* \page listLIST_IS_EMPTY listLIST_IS_EMPTY
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE )
|
||||
|
||||
/*
|
||||
* Access macro to return the number of items in the list.
|
||||
*/
|
||||
#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems )
|
||||
|
||||
/*
|
||||
* Access function to obtain the owner of the next entry in a list.
|
||||
*
|
||||
* The list member pxIndex is used to walk through a list. Calling
|
||||
* listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list
|
||||
* and returns that entry's pxOwner parameter. Using multiple calls to this
|
||||
* function it is therefore possible to move through every item contained in
|
||||
* a list.
|
||||
*
|
||||
* The pxOwner parameter of a list item is a pointer to the object that owns
|
||||
* the list item. In the scheduler this is normally a task control block.
|
||||
* The pxOwner parameter effectively creates a two way link between the list
|
||||
* item and its owner.
|
||||
*
|
||||
* @param pxTCB pxTCB is set to the address of the owner of the next list item.
|
||||
* @param pxList The list from which the next item owner is to be returned.
|
||||
*
|
||||
* \page listGET_OWNER_OF_NEXT_ENTRY listGET_OWNER_OF_NEXT_ENTRY
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#if ( configNUMBER_OF_CORES == 1 )
|
||||
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
|
||||
do { \
|
||||
List_t * const pxConstList = ( pxList ); \
|
||||
/* Increment the index to the next item and return the item, ensuring */ \
|
||||
/* we don't return the marker used at the end of the list. */ \
|
||||
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
|
||||
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
|
||||
{ \
|
||||
( pxConstList )->pxIndex = ( pxConstList )->xListEnd.pxNext; \
|
||||
} \
|
||||
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
|
||||
} while( 0 )
|
||||
#else /* #if ( configNUMBER_OF_CORES == 1 ) */
|
||||
|
||||
/* This function is not required in SMP. FreeRTOS SMP scheduler doesn't use
|
||||
* pxIndex and it should always point to the xListEnd. Not defining this macro
|
||||
* here to prevent updating pxIndex.
|
||||
*/
|
||||
#endif /* #if ( configNUMBER_OF_CORES == 1 ) */
|
||||
|
||||
/*
|
||||
* Version of uxListRemove() that does not return a value. Provided as a slight
|
||||
* optimisation for xTaskIncrementTick() by being inline.
|
||||
*
|
||||
* Remove an item from a list. The list item has a pointer to the list that
|
||||
* it is in, so only the list item need be passed into the function.
|
||||
*
|
||||
* @param uxListRemove The item to be removed. The item will remove itself from
|
||||
* the list pointed to by it's pxContainer parameter.
|
||||
*
|
||||
* @return The number of items that remain in the list after the list item has
|
||||
* been removed.
|
||||
*
|
||||
* \page listREMOVE_ITEM listREMOVE_ITEM
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#define listREMOVE_ITEM( pxItemToRemove ) \
|
||||
do { \
|
||||
/* The list item knows which list it is in. Obtain the list from the list \
|
||||
* item. */ \
|
||||
List_t * const pxList = ( pxItemToRemove )->pxContainer; \
|
||||
\
|
||||
( pxItemToRemove )->pxNext->pxPrevious = ( pxItemToRemove )->pxPrevious; \
|
||||
( pxItemToRemove )->pxPrevious->pxNext = ( pxItemToRemove )->pxNext; \
|
||||
/* Make sure the index is left pointing to a valid item. */ \
|
||||
if( pxList->pxIndex == ( pxItemToRemove ) ) \
|
||||
{ \
|
||||
pxList->pxIndex = ( pxItemToRemove )->pxPrevious; \
|
||||
} \
|
||||
\
|
||||
( pxItemToRemove )->pxContainer = NULL; \
|
||||
( ( pxList )->uxNumberOfItems ) = ( UBaseType_t ) ( ( ( pxList )->uxNumberOfItems ) - 1U ); \
|
||||
} while( 0 )
|
||||
|
||||
/*
|
||||
* Inline version of vListInsertEnd() to provide slight optimisation for
|
||||
* xTaskIncrementTick().
|
||||
*
|
||||
* Insert a list item into a list. The item will be inserted in a position
|
||||
* such that it will be the last item within the list returned by multiple
|
||||
* calls to listGET_OWNER_OF_NEXT_ENTRY.
|
||||
*
|
||||
* The list member pxIndex is used to walk through a list. Calling
|
||||
* listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list.
|
||||
* Placing an item in a list using vListInsertEnd effectively places the item
|
||||
* in the list position pointed to by pxIndex. This means that every other
|
||||
* item within the list will be returned by listGET_OWNER_OF_NEXT_ENTRY before
|
||||
* the pxIndex parameter again points to the item being inserted.
|
||||
*
|
||||
* @param pxList The list into which the item is to be inserted.
|
||||
*
|
||||
* @param pxNewListItem The list item to be inserted into the list.
|
||||
*
|
||||
* \page listINSERT_END listINSERT_END
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#define listINSERT_END( pxList, pxNewListItem ) \
|
||||
do { \
|
||||
ListItem_t * const pxIndex = ( pxList )->pxIndex; \
|
||||
\
|
||||
/* Only effective when configASSERT() is also defined, these tests may catch \
|
||||
* the list data structures being overwritten in memory. They will not catch \
|
||||
* data errors caused by incorrect configuration or use of FreeRTOS. */ \
|
||||
listTEST_LIST_INTEGRITY( ( pxList ) ); \
|
||||
listTEST_LIST_ITEM_INTEGRITY( ( pxNewListItem ) ); \
|
||||
\
|
||||
/* Insert a new list item into ( pxList ), but rather than sort the list, \
|
||||
* makes the new list item the last item to be removed by a call to \
|
||||
* listGET_OWNER_OF_NEXT_ENTRY(). */ \
|
||||
( pxNewListItem )->pxNext = pxIndex; \
|
||||
( pxNewListItem )->pxPrevious = pxIndex->pxPrevious; \
|
||||
\
|
||||
pxIndex->pxPrevious->pxNext = ( pxNewListItem ); \
|
||||
pxIndex->pxPrevious = ( pxNewListItem ); \
|
||||
\
|
||||
/* Remember which list the item is in. */ \
|
||||
( pxNewListItem )->pxContainer = ( pxList ); \
|
||||
\
|
||||
( ( pxList )->uxNumberOfItems ) = ( UBaseType_t ) ( ( ( pxList )->uxNumberOfItems ) + 1U ); \
|
||||
} while( 0 )
|
||||
|
||||
/*
|
||||
* Access function to obtain the owner of the first entry in a list. Lists
|
||||
* are normally sorted in ascending item value order.
|
||||
*
|
||||
* This function returns the pxOwner member of the first item in the list.
|
||||
* The pxOwner parameter of a list item is a pointer to the object that owns
|
||||
* the list item. In the scheduler this is normally a task control block.
|
||||
* The pxOwner parameter effectively creates a two way link between the list
|
||||
* item and its owner.
|
||||
*
|
||||
* @param pxList The list from which the owner of the head item is to be
|
||||
* returned.
|
||||
*
|
||||
* \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( ( &( ( pxList )->xListEnd ) )->pxNext->pvOwner )
|
||||
|
||||
/*
|
||||
* Check to see if a list item is within a list. The list item maintains a
|
||||
* "container" pointer that points to the list it is in. All this macro does
|
||||
* is check to see if the container and the list match.
|
||||
*
|
||||
* @param pxList The list we want to know if the list item is within.
|
||||
* @param pxListItem The list item we want to know if is in the list.
|
||||
* @return pdTRUE if the list item is in the list, otherwise pdFALSE.
|
||||
*/
|
||||
#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) )
|
||||
|
||||
/*
|
||||
* Return the list a list item is contained within (referenced from).
|
||||
*
|
||||
* @param pxListItem The list item being queried.
|
||||
* @return A pointer to the List_t object that references the pxListItem
|
||||
*/
|
||||
#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer )
|
||||
|
||||
/*
|
||||
* This provides a crude means of knowing if a list has been initialised, as
|
||||
* pxList->xListEnd.xItemValue is set to portMAX_DELAY by the vListInitialise()
|
||||
* function.
|
||||
*/
|
||||
#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )
|
||||
|
||||
/*
|
||||
* Must be called before a list is used! This initialises all the members
|
||||
* of the list structure and inserts the xListEnd item into the list as a
|
||||
* marker to the back of the list.
|
||||
*
|
||||
* @param pxList Pointer to the list being initialised.
|
||||
*
|
||||
* \page vListInitialise vListInitialise
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
void vListInitialise( List_t * const pxList ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/*
|
||||
* Must be called before a list item is used. This sets the list container to
|
||||
* null so the item does not think that it is already contained in a list.
|
||||
*
|
||||
* @param pxItem Pointer to the list item being initialised.
|
||||
*
|
||||
* \page vListInitialiseItem vListInitialiseItem
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
void vListInitialiseItem( ListItem_t * const pxItem ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/*
|
||||
* Insert a list item into a list. The item will be inserted into the list in
|
||||
* a position determined by its item value (ascending item value order).
|
||||
*
|
||||
* @param pxList The list into which the item is to be inserted.
|
||||
*
|
||||
* @param pxNewListItem The item that is to be placed in the list.
|
||||
*
|
||||
* \page vListInsert vListInsert
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
void vListInsert( List_t * const pxList,
|
||||
ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/*
|
||||
* Insert a list item into a list. The item will be inserted in a position
|
||||
* such that it will be the last item within the list returned by multiple
|
||||
* calls to listGET_OWNER_OF_NEXT_ENTRY.
|
||||
*
|
||||
* The list member pxIndex is used to walk through a list. Calling
|
||||
* listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list.
|
||||
* Placing an item in a list using vListInsertEnd effectively places the item
|
||||
* in the list position pointed to by pxIndex. This means that every other
|
||||
* item within the list will be returned by listGET_OWNER_OF_NEXT_ENTRY before
|
||||
* the pxIndex parameter again points to the item being inserted.
|
||||
*
|
||||
* @param pxList The list into which the item is to be inserted.
|
||||
*
|
||||
* @param pxNewListItem The list item to be inserted into the list.
|
||||
*
|
||||
* \page vListInsertEnd vListInsertEnd
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
void vListInsertEnd( List_t * const pxList,
|
||||
ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/*
|
||||
* Remove an item from a list. The list item has a pointer to the list that
|
||||
* it is in, so only the list item need be passed into the function.
|
||||
*
|
||||
* @param uxListRemove The item to be removed. The item will remove itself from
|
||||
* the list pointed to by it's pxContainer parameter.
|
||||
*
|
||||
* @return The number of items that remain in the list after the list item has
|
||||
* been removed.
|
||||
*
|
||||
* \page uxListRemove uxListRemove
|
||||
* \ingroup LinkedList
|
||||
*/
|
||||
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
#endif /* ifndef LIST_H */
|
||||
+967
@@ -0,0 +1,967 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Message buffers build functionality on top of FreeRTOS stream buffers.
|
||||
* Whereas stream buffers are used to send a continuous stream of data from one
|
||||
* task or interrupt to another, message buffers are used to send variable
|
||||
* length discrete messages from one task or interrupt to another. Their
|
||||
* implementation is light weight, making them particularly suited for interrupt
|
||||
* to task and core to core communication scenarios.
|
||||
*
|
||||
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||
* implementation (so also the message buffer implementation, as message buffers
|
||||
* are built on top of stream buffers) assumes there is only one task or
|
||||
* interrupt that will write to the buffer (the writer), and only one task or
|
||||
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||
* multiple different readers. If there are to be multiple different writers
|
||||
* then the application writer must serialize calls to writing API functions
|
||||
* (such as xStreamBufferSend()). Likewise, if there are to be multiple
|
||||
* different readers then the application writer must serialize calls to reading
|
||||
* API functions (such as xStreamBufferReceive()). One way to achieve such
|
||||
* serialization in single core or SMP kernel is to place each API call inside a
|
||||
* critical section and use a block time of 0.
|
||||
*
|
||||
* Message buffers hold variable length messages. To enable that, when a
|
||||
* message is written to the message buffer an additional sizeof( size_t ) bytes
|
||||
* are also written to store the message's length (that happens internally, with
|
||||
* the API function). sizeof( size_t ) is typically 4 bytes on a 32-bit
|
||||
* architecture, so writing a 10 byte message to a message buffer on a 32-bit
|
||||
* architecture will actually reduce the available space in the message buffer
|
||||
* by 14 bytes (10 byte are used by the message, and 4 bytes to hold the length
|
||||
* of the message).
|
||||
*/
|
||||
|
||||
#ifndef FREERTOS_MESSAGE_BUFFER_H
|
||||
#define FREERTOS_MESSAGE_BUFFER_H
|
||||
|
||||
#ifndef INC_FREERTOS_H
|
||||
#error "include FreeRTOS.h must appear in source files before include message_buffer.h"
|
||||
#endif
|
||||
|
||||
/* Message buffers are built onto of stream buffers. */
|
||||
#include "stream_buffer.h"
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#if defined( __cplusplus )
|
||||
extern "C" {
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/**
|
||||
* Type by which message buffers are referenced. For example, a call to
|
||||
* xMessageBufferCreate() returns an MessageBufferHandle_t variable that can
|
||||
* then be used as a parameter to xMessageBufferSend(), xMessageBufferReceive(),
|
||||
* etc. Message buffer is essentially built as a stream buffer hence its handle
|
||||
* is also set to same type as a stream buffer handle.
|
||||
*/
|
||||
typedef StreamBufferHandle_t MessageBufferHandle_t;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
*
|
||||
* @code{c}
|
||||
* MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes );
|
||||
* @endcode
|
||||
*
|
||||
* Creates a new message buffer using dynamically allocated memory. See
|
||||
* xMessageBufferCreateStatic() for a version that uses statically allocated
|
||||
* memory (memory that is allocated at compile time).
|
||||
*
|
||||
* configSUPPORT_DYNAMIC_ALLOCATION must be set to 1 or left undefined in
|
||||
* FreeRTOSConfig.h for xMessageBufferCreate() to be available.
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferCreate() to be available.
|
||||
*
|
||||
* @param xBufferSizeBytes The total number of bytes (not messages) the message
|
||||
* buffer will be able to hold at any one time. When a message is written to
|
||||
* the message buffer an additional sizeof( size_t ) bytes are also written to
|
||||
* store the message's length. sizeof( size_t ) is typically 4 bytes on a
|
||||
* 32-bit architecture, so on most 32-bit architectures a 10 byte message will
|
||||
* take up 14 bytes of message buffer space.
|
||||
*
|
||||
* @param pxSendCompletedCallback Callback invoked when a send operation to the
|
||||
* message buffer is complete. If the parameter is NULL or xMessageBufferCreate()
|
||||
* is called without the parameter, then it will use the default implementation
|
||||
* provided by sbSEND_COMPLETED macro. To enable the callback,
|
||||
* configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.
|
||||
*
|
||||
* @param pxReceiveCompletedCallback Callback invoked when a receive operation from
|
||||
* the message buffer is complete. If the parameter is NULL or xMessageBufferCreate()
|
||||
* is called without the parameter, it will use the default implementation provided
|
||||
* by sbRECEIVE_COMPLETED macro. To enable the callback,
|
||||
* configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.
|
||||
*
|
||||
* @return If NULL is returned, then the message buffer cannot be created
|
||||
* because there is insufficient heap memory available for FreeRTOS to allocate
|
||||
* the message buffer data structures and storage area. A non-NULL value being
|
||||
* returned indicates that the message buffer has been created successfully -
|
||||
* the returned value should be stored as the handle to the created message
|
||||
* buffer.
|
||||
*
|
||||
* Example use:
|
||||
* @code{c}
|
||||
*
|
||||
* void vAFunction( void )
|
||||
* {
|
||||
* MessageBufferHandle_t xMessageBuffer;
|
||||
* const size_t xMessageBufferSizeBytes = 100;
|
||||
*
|
||||
* // Create a message buffer that can hold 100 bytes. The memory used to hold
|
||||
* // both the message buffer structure and the messages themselves is allocated
|
||||
* // dynamically. Each message added to the buffer consumes an additional 4
|
||||
* // bytes which are used to hold the length of the message.
|
||||
* xMessageBuffer = xMessageBufferCreate( xMessageBufferSizeBytes );
|
||||
*
|
||||
* if( xMessageBuffer == NULL )
|
||||
* {
|
||||
* // There was not enough heap memory space available to create the
|
||||
* // message buffer.
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* // The message buffer was created successfully and can now be used.
|
||||
* }
|
||||
*
|
||||
* @endcode
|
||||
* \defgroup xMessageBufferCreate xMessageBufferCreate
|
||||
* \ingroup MessageBufferManagement
|
||||
*/
|
||||
#define xMessageBufferCreate( xBufferSizeBytes ) \
|
||||
xStreamBufferGenericCreate( ( xBufferSizeBytes ), ( size_t ) 0, sbTYPE_MESSAGE_BUFFER, NULL, NULL )
|
||||
|
||||
#if ( configUSE_SB_COMPLETED_CALLBACK == 1 )
|
||||
#define xMessageBufferCreateWithCallback( xBufferSizeBytes, pxSendCompletedCallback, pxReceiveCompletedCallback ) \
|
||||
xStreamBufferGenericCreate( ( xBufferSizeBytes ), ( size_t ) 0, sbTYPE_MESSAGE_BUFFER, ( pxSendCompletedCallback ), ( pxReceiveCompletedCallback ) )
|
||||
#endif
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
*
|
||||
* @code{c}
|
||||
* MessageBufferHandle_t xMessageBufferCreateStatic( size_t xBufferSizeBytes,
|
||||
* uint8_t *pucMessageBufferStorageArea,
|
||||
* StaticMessageBuffer_t *pxStaticMessageBuffer );
|
||||
* @endcode
|
||||
* Creates a new message buffer using statically allocated memory. See
|
||||
* xMessageBufferCreate() for a version that uses dynamically allocated memory.
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferCreateStatic() to be available.
|
||||
*
|
||||
* @param xBufferSizeBytes The size, in bytes, of the buffer pointed to by the
|
||||
* pucMessageBufferStorageArea parameter. When a message is written to the
|
||||
* message buffer an additional sizeof( size_t ) bytes are also written to store
|
||||
* the message's length. sizeof( size_t ) is typically 4 bytes on a 32-bit
|
||||
* architecture, so on most 32-bit architecture a 10 byte message will take up
|
||||
* 14 bytes of message buffer space. The maximum number of bytes that can be
|
||||
* stored in the message buffer is actually (xBufferSizeBytes - 1).
|
||||
*
|
||||
* @param pucMessageBufferStorageArea Must point to a uint8_t array that is at
|
||||
* least xBufferSizeBytes big. This is the array to which messages are
|
||||
* copied when they are written to the message buffer.
|
||||
*
|
||||
* @param pxStaticMessageBuffer Must point to a variable of type
|
||||
* StaticMessageBuffer_t, which will be used to hold the message buffer's data
|
||||
* structure.
|
||||
*
|
||||
* @param pxSendCompletedCallback Callback invoked when a new message is sent to the message buffer.
|
||||
* If the parameter is NULL or xMessageBufferCreate() is called without the parameter, then it will use the default
|
||||
* implementation provided by sbSEND_COMPLETED macro. To enable the callback,
|
||||
* configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.
|
||||
*
|
||||
* @param pxReceiveCompletedCallback Callback invoked when a message is read from a
|
||||
* message buffer. If the parameter is NULL or xMessageBufferCreate() is called without the parameter, it will
|
||||
* use the default implementation provided by sbRECEIVE_COMPLETED macro. To enable the callback,
|
||||
* configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.
|
||||
*
|
||||
* @return If the message buffer is created successfully then a handle to the
|
||||
* created message buffer is returned. If either pucMessageBufferStorageArea or
|
||||
* pxStaticmessageBuffer are NULL then NULL is returned.
|
||||
*
|
||||
* Example use:
|
||||
* @code{c}
|
||||
*
|
||||
* // Used to dimension the array used to hold the messages. The available space
|
||||
* // will actually be one less than this, so 999.
|
||||
#define STORAGE_SIZE_BYTES 1000
|
||||
*
|
||||
* // Defines the memory that will actually hold the messages within the message
|
||||
* // buffer.
|
||||
* static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ];
|
||||
*
|
||||
* // The variable used to hold the message buffer structure.
|
||||
* StaticMessageBuffer_t xMessageBufferStruct;
|
||||
*
|
||||
* void MyFunction( void )
|
||||
* {
|
||||
* MessageBufferHandle_t xMessageBuffer;
|
||||
*
|
||||
* xMessageBuffer = xMessageBufferCreateStatic( sizeof( ucStorageBuffer ),
|
||||
* ucStorageBuffer,
|
||||
* &xMessageBufferStruct );
|
||||
*
|
||||
* // As neither the pucMessageBufferStorageArea or pxStaticMessageBuffer
|
||||
* // parameters were NULL, xMessageBuffer will not be NULL, and can be used to
|
||||
* // reference the created message buffer in other message buffer API calls.
|
||||
*
|
||||
* // Other code that uses the message buffer can go here.
|
||||
* }
|
||||
*
|
||||
* @endcode
|
||||
* \defgroup xMessageBufferCreateStatic xMessageBufferCreateStatic
|
||||
* \ingroup MessageBufferManagement
|
||||
*/
|
||||
#define xMessageBufferCreateStatic( xBufferSizeBytes, pucMessageBufferStorageArea, pxStaticMessageBuffer ) \
|
||||
xStreamBufferGenericCreateStatic( ( xBufferSizeBytes ), 0, sbTYPE_MESSAGE_BUFFER, ( pucMessageBufferStorageArea ), ( pxStaticMessageBuffer ), NULL, NULL )
|
||||
|
||||
#if ( configUSE_SB_COMPLETED_CALLBACK == 1 )
|
||||
#define xMessageBufferCreateStaticWithCallback( xBufferSizeBytes, pucMessageBufferStorageArea, pxStaticMessageBuffer, pxSendCompletedCallback, pxReceiveCompletedCallback ) \
|
||||
xStreamBufferGenericCreateStatic( ( xBufferSizeBytes ), 0, sbTYPE_MESSAGE_BUFFER, ( pucMessageBufferStorageArea ), ( pxStaticMessageBuffer ), ( pxSendCompletedCallback ), ( pxReceiveCompletedCallback ) )
|
||||
#endif
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
*
|
||||
* @code{c}
|
||||
* BaseType_t xMessageBufferGetStaticBuffers( MessageBufferHandle_t xMessageBuffer,
|
||||
* uint8_t ** ppucMessageBufferStorageArea,
|
||||
* StaticMessageBuffer_t ** ppxStaticMessageBuffer );
|
||||
* @endcode
|
||||
*
|
||||
* Retrieve pointers to a statically created message buffer's data structure
|
||||
* buffer and storage area buffer. These are the same buffers that are supplied
|
||||
* at the time of creation.
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferGetStaticBuffers() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The message buffer for which to retrieve the buffers.
|
||||
*
|
||||
* @param ppucMessageBufferStorageArea Used to return a pointer to the
|
||||
* message buffer's storage area buffer.
|
||||
*
|
||||
* @param ppxStaticMessageBuffer Used to return a pointer to the message
|
||||
* buffer's data structure buffer.
|
||||
*
|
||||
* @return pdTRUE if buffers were retrieved, pdFALSE otherwise..
|
||||
*
|
||||
* \defgroup xMessageBufferGetStaticBuffers xMessageBufferGetStaticBuffers
|
||||
* \ingroup MessageBufferManagement
|
||||
*/
|
||||
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||
#define xMessageBufferGetStaticBuffers( xMessageBuffer, ppucMessageBufferStorageArea, ppxStaticMessageBuffer ) \
|
||||
xStreamBufferGetStaticBuffers( ( xMessageBuffer ), ( ppucMessageBufferStorageArea ), ( ppxStaticMessageBuffer ) )
|
||||
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
*
|
||||
* @code{c}
|
||||
* size_t xMessageBufferSend( MessageBufferHandle_t xMessageBuffer,
|
||||
* const void *pvTxData,
|
||||
* size_t xDataLengthBytes,
|
||||
* TickType_t xTicksToWait );
|
||||
* @endcode
|
||||
*
|
||||
* Sends a discrete message to the message buffer. The message can be any
|
||||
* length that fits within the buffer's free space, and is copied into the
|
||||
* buffer.
|
||||
*
|
||||
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||
* implementation (so also the message buffer implementation, as message buffers
|
||||
* are built on top of stream buffers) assumes there is only one task or
|
||||
* interrupt that will write to the buffer (the writer), and only one task or
|
||||
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||
* multiple different readers. If there are to be multiple different writers
|
||||
* then the application writer must serialize calls to writing API functions
|
||||
* (such as xStreamBufferSend()). Likewise, if there are to be multiple
|
||||
* different readers then the application writer must serialize calls to reading
|
||||
* API functions (such as xStreamBufferReceive()). One way to achieve such
|
||||
* serialization in single core or SMP kernel is to place each API call inside a
|
||||
* critical section and use a block time of 0.
|
||||
*
|
||||
* Use xMessageBufferSend() to write to a message buffer from a task. Use
|
||||
* xMessageBufferSendFromISR() to write to a message buffer from an interrupt
|
||||
* service routine (ISR).
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferSend() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the message buffer to which a message is
|
||||
* being sent.
|
||||
*
|
||||
* @param pvTxData A pointer to the message that is to be copied into the
|
||||
* message buffer.
|
||||
*
|
||||
* @param xDataLengthBytes The length of the message. That is, the number of
|
||||
* bytes to copy from pvTxData into the message buffer. When a message is
|
||||
* written to the message buffer an additional sizeof( size_t ) bytes are also
|
||||
* written to store the message's length. sizeof( size_t ) is typically 4 bytes
|
||||
* on a 32-bit architecture, so on most 32-bit architecture setting
|
||||
* xDataLengthBytes to 20 will reduce the free space in the message buffer by 24
|
||||
* bytes (20 bytes of message data and 4 bytes to hold the message length).
|
||||
*
|
||||
* @param xTicksToWait The maximum amount of time the calling task should remain
|
||||
* in the Blocked state to wait for enough space to become available in the
|
||||
* message buffer, should the message buffer have insufficient space when
|
||||
* xMessageBufferSend() is called. The calling task will never block if
|
||||
* xTicksToWait is zero. The block time is specified in tick periods, so the
|
||||
* absolute time it represents is dependent on the tick frequency. The macro
|
||||
* pdMS_TO_TICKS() can be used to convert a time specified in milliseconds into
|
||||
* a time specified in ticks. Setting xTicksToWait to portMAX_DELAY will cause
|
||||
* the task to wait indefinitely (without timing out), provided
|
||||
* INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h. Tasks do not use any
|
||||
* CPU time when they are in the Blocked state.
|
||||
*
|
||||
* @return The number of bytes written to the message buffer. If the call to
|
||||
* xMessageBufferSend() times out before there was enough space to write the
|
||||
* message into the message buffer then zero is returned. If the call did not
|
||||
* time out then xDataLengthBytes is returned.
|
||||
*
|
||||
* Example use:
|
||||
* @code{c}
|
||||
* void vAFunction( MessageBufferHandle_t xMessageBuffer )
|
||||
* {
|
||||
* size_t xBytesSent;
|
||||
* uint8_t ucArrayToSend[] = { 0, 1, 2, 3 };
|
||||
* char *pcStringToSend = "String to send";
|
||||
* const TickType_t x100ms = pdMS_TO_TICKS( 100 );
|
||||
*
|
||||
* // Send an array to the message buffer, blocking for a maximum of 100ms to
|
||||
* // wait for enough space to be available in the message buffer.
|
||||
* xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms );
|
||||
*
|
||||
* if( xBytesSent != sizeof( ucArrayToSend ) )
|
||||
* {
|
||||
* // The call to xMessageBufferSend() times out before there was enough
|
||||
* // space in the buffer for the data to be written.
|
||||
* }
|
||||
*
|
||||
* // Send the string to the message buffer. Return immediately if there is
|
||||
* // not enough space in the buffer.
|
||||
* xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 );
|
||||
*
|
||||
* if( xBytesSent != strlen( pcStringToSend ) )
|
||||
* {
|
||||
* // The string could not be added to the message buffer because there was
|
||||
* // not enough free space in the buffer.
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup xMessageBufferSend xMessageBufferSend
|
||||
* \ingroup MessageBufferManagement
|
||||
*/
|
||||
#define xMessageBufferSend( xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) \
|
||||
xStreamBufferSend( ( xMessageBuffer ), ( pvTxData ), ( xDataLengthBytes ), ( xTicksToWait ) )
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
*
|
||||
* @code{c}
|
||||
* size_t xMessageBufferSendFromISR( MessageBufferHandle_t xMessageBuffer,
|
||||
* const void *pvTxData,
|
||||
* size_t xDataLengthBytes,
|
||||
* BaseType_t *pxHigherPriorityTaskWoken );
|
||||
* @endcode
|
||||
*
|
||||
* Interrupt safe version of the API function that sends a discrete message to
|
||||
* the message buffer. The message can be any length that fits within the
|
||||
* buffer's free space, and is copied into the buffer.
|
||||
*
|
||||
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||
* implementation (so also the message buffer implementation, as message buffers
|
||||
* are built on top of stream buffers) assumes there is only one task or
|
||||
* interrupt that will write to the buffer (the writer), and only one task or
|
||||
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||
* multiple different readers. If there are to be multiple different writers
|
||||
* then the application writer must serialize calls to writing API functions
|
||||
* (such as xStreamBufferSend()). Likewise, if there are to be multiple
|
||||
* different readers then the application writer must serialize calls to reading
|
||||
* API functions (such as xStreamBufferReceive()). One way to achieve such
|
||||
* serialization in single core or SMP kernel is to place each API call inside a
|
||||
* critical section and use a block time of 0.
|
||||
*
|
||||
* Use xMessageBufferSend() to write to a message buffer from a task. Use
|
||||
* xMessageBufferSendFromISR() to write to a message buffer from an interrupt
|
||||
* service routine (ISR).
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferSendFromISR() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the message buffer to which a message is
|
||||
* being sent.
|
||||
*
|
||||
* @param pvTxData A pointer to the message that is to be copied into the
|
||||
* message buffer.
|
||||
*
|
||||
* @param xDataLengthBytes The length of the message. That is, the number of
|
||||
* bytes to copy from pvTxData into the message buffer. When a message is
|
||||
* written to the message buffer an additional sizeof( size_t ) bytes are also
|
||||
* written to store the message's length. sizeof( size_t ) is typically 4 bytes
|
||||
* on a 32-bit architecture, so on most 32-bit architecture setting
|
||||
* xDataLengthBytes to 20 will reduce the free space in the message buffer by 24
|
||||
* bytes (20 bytes of message data and 4 bytes to hold the message length).
|
||||
*
|
||||
* @param pxHigherPriorityTaskWoken It is possible that a message buffer will
|
||||
* have a task blocked on it waiting for data. Calling
|
||||
* xMessageBufferSendFromISR() can make data available, and so cause a task that
|
||||
* was waiting for data to leave the Blocked state. If calling
|
||||
* xMessageBufferSendFromISR() causes a task to leave the Blocked state, and the
|
||||
* unblocked task has a priority higher than the currently executing task (the
|
||||
* task that was interrupted), then, internally, xMessageBufferSendFromISR()
|
||||
* will set *pxHigherPriorityTaskWoken to pdTRUE. If
|
||||
* xMessageBufferSendFromISR() sets this value to pdTRUE, then normally a
|
||||
* context switch should be performed before the interrupt is exited. This will
|
||||
* ensure that the interrupt returns directly to the highest priority Ready
|
||||
* state task. *pxHigherPriorityTaskWoken should be set to pdFALSE before it
|
||||
* is passed into the function. See the code example below for an example.
|
||||
*
|
||||
* @return The number of bytes actually written to the message buffer. If the
|
||||
* message buffer didn't have enough free space for the message to be stored
|
||||
* then 0 is returned, otherwise xDataLengthBytes is returned.
|
||||
*
|
||||
* Example use:
|
||||
* @code{c}
|
||||
* // A message buffer that has already been created.
|
||||
* MessageBufferHandle_t xMessageBuffer;
|
||||
*
|
||||
* void vAnInterruptServiceRoutine( void )
|
||||
* {
|
||||
* size_t xBytesSent;
|
||||
* char *pcStringToSend = "String to send";
|
||||
* BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
|
||||
*
|
||||
* // Attempt to send the string to the message buffer.
|
||||
* xBytesSent = xMessageBufferSendFromISR( xMessageBuffer,
|
||||
* ( void * ) pcStringToSend,
|
||||
* strlen( pcStringToSend ),
|
||||
* &xHigherPriorityTaskWoken );
|
||||
*
|
||||
* if( xBytesSent != strlen( pcStringToSend ) )
|
||||
* {
|
||||
* // The string could not be added to the message buffer because there was
|
||||
* // not enough free space in the buffer.
|
||||
* }
|
||||
*
|
||||
* // If xHigherPriorityTaskWoken was set to pdTRUE inside
|
||||
* // xMessageBufferSendFromISR() then a task that has a priority above the
|
||||
* // priority of the currently executing task was unblocked and a context
|
||||
* // switch should be performed to ensure the ISR returns to the unblocked
|
||||
* // task. In most FreeRTOS ports this is done by simply passing
|
||||
* // xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the
|
||||
* // variables value, and perform the context switch if necessary. Check the
|
||||
* // documentation for the port in use for port specific instructions.
|
||||
* portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup xMessageBufferSendFromISR xMessageBufferSendFromISR
|
||||
* \ingroup MessageBufferManagement
|
||||
*/
|
||||
#define xMessageBufferSendFromISR( xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) \
|
||||
xStreamBufferSendFromISR( ( xMessageBuffer ), ( pvTxData ), ( xDataLengthBytes ), ( pxHigherPriorityTaskWoken ) )
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
*
|
||||
* @code{c}
|
||||
* size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer,
|
||||
* void *pvRxData,
|
||||
* size_t xBufferLengthBytes,
|
||||
* TickType_t xTicksToWait );
|
||||
* @endcode
|
||||
*
|
||||
* Receives a discrete message from a message buffer. Messages can be of
|
||||
* variable length and are copied out of the buffer.
|
||||
*
|
||||
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||
* implementation (so also the message buffer implementation, as message buffers
|
||||
* are built on top of stream buffers) assumes there is only one task or
|
||||
* interrupt that will write to the buffer (the writer), and only one task or
|
||||
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||
* multiple different readers. If there are to be multiple different writers
|
||||
* then the application writer must serialize calls to writing API functions
|
||||
* (such as xStreamBufferSend()). Likewise, if there are to be multiple
|
||||
* different readers then the application writer must serialize calls to reading
|
||||
* API functions (such as xStreamBufferReceive()). One way to achieve such
|
||||
* serialization in single core or SMP kernel is to place each API call inside a
|
||||
* critical section and use a block time of 0.
|
||||
*
|
||||
* Use xMessageBufferReceive() to read from a message buffer from a task. Use
|
||||
* xMessageBufferReceiveFromISR() to read from a message buffer from an
|
||||
* interrupt service routine (ISR).
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferReceive() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the message buffer from which a message
|
||||
* is being received.
|
||||
*
|
||||
* @param pvRxData A pointer to the buffer into which the received message is
|
||||
* to be copied.
|
||||
*
|
||||
* @param xBufferLengthBytes The length of the buffer pointed to by the pvRxData
|
||||
* parameter. This sets the maximum length of the message that can be received.
|
||||
* If xBufferLengthBytes is too small to hold the next message then the message
|
||||
* will be left in the message buffer and 0 will be returned.
|
||||
*
|
||||
* @param xTicksToWait The maximum amount of time the task should remain in the
|
||||
* Blocked state to wait for a message, should the message buffer be empty.
|
||||
* xMessageBufferReceive() will return immediately if xTicksToWait is zero and
|
||||
* the message buffer is empty. The block time is specified in tick periods, so
|
||||
* the absolute time it represents is dependent on the tick frequency. The
|
||||
* macro pdMS_TO_TICKS() can be used to convert a time specified in milliseconds
|
||||
* into a time specified in ticks. Setting xTicksToWait to portMAX_DELAY will
|
||||
* cause the task to wait indefinitely (without timing out), provided
|
||||
* INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h. Tasks do not use any
|
||||
* CPU time when they are in the Blocked state.
|
||||
*
|
||||
* @return The length, in bytes, of the message read from the message buffer, if
|
||||
* any. If xMessageBufferReceive() times out before a message became available
|
||||
* then zero is returned. If the length of the message is greater than
|
||||
* xBufferLengthBytes then the message will be left in the message buffer and
|
||||
* zero is returned.
|
||||
*
|
||||
* Example use:
|
||||
* @code{c}
|
||||
* void vAFunction( MessageBuffer_t xMessageBuffer )
|
||||
* {
|
||||
* uint8_t ucRxData[ 20 ];
|
||||
* size_t xReceivedBytes;
|
||||
* const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
|
||||
*
|
||||
* // Receive the next message from the message buffer. Wait in the Blocked
|
||||
* // state (so not using any CPU processing time) for a maximum of 100ms for
|
||||
* // a message to become available.
|
||||
* xReceivedBytes = xMessageBufferReceive( xMessageBuffer,
|
||||
* ( void * ) ucRxData,
|
||||
* sizeof( ucRxData ),
|
||||
* xBlockTime );
|
||||
*
|
||||
* if( xReceivedBytes > 0 )
|
||||
* {
|
||||
* // A ucRxData contains a message that is xReceivedBytes long. Process
|
||||
* // the message here....
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup xMessageBufferReceive xMessageBufferReceive
|
||||
* \ingroup MessageBufferManagement
|
||||
*/
|
||||
#define xMessageBufferReceive( xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) \
|
||||
xStreamBufferReceive( ( xMessageBuffer ), ( pvRxData ), ( xBufferLengthBytes ), ( xTicksToWait ) )
|
||||
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
*
|
||||
* @code{c}
|
||||
* size_t xMessageBufferReceiveFromISR( MessageBufferHandle_t xMessageBuffer,
|
||||
* void *pvRxData,
|
||||
* size_t xBufferLengthBytes,
|
||||
* BaseType_t *pxHigherPriorityTaskWoken );
|
||||
* @endcode
|
||||
*
|
||||
* An interrupt safe version of the API function that receives a discrete
|
||||
* message from a message buffer. Messages can be of variable length and are
|
||||
* copied out of the buffer.
|
||||
*
|
||||
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||
* implementation (so also the message buffer implementation, as message buffers
|
||||
* are built on top of stream buffers) assumes there is only one task or
|
||||
* interrupt that will write to the buffer (the writer), and only one task or
|
||||
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||
* multiple different readers. If there are to be multiple different writers
|
||||
* then the application writer must serialize calls to writing API functions
|
||||
* (such as xStreamBufferSend()). Likewise, if there are to be multiple
|
||||
* different readers then the application writer must serialize calls to reading
|
||||
* API functions (such as xStreamBufferReceive()). One way to achieve such
|
||||
* serialization in single core or SMP kernel is to place each API call inside a
|
||||
* critical section and use a block time of 0.
|
||||
*
|
||||
* Use xMessageBufferReceive() to read from a message buffer from a task. Use
|
||||
* xMessageBufferReceiveFromISR() to read from a message buffer from an
|
||||
* interrupt service routine (ISR).
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferReceiveFromISR() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the message buffer from which a message
|
||||
* is being received.
|
||||
*
|
||||
* @param pvRxData A pointer to the buffer into which the received message is
|
||||
* to be copied.
|
||||
*
|
||||
* @param xBufferLengthBytes The length of the buffer pointed to by the pvRxData
|
||||
* parameter. This sets the maximum length of the message that can be received.
|
||||
* If xBufferLengthBytes is too small to hold the next message then the message
|
||||
* will be left in the message buffer and 0 will be returned.
|
||||
*
|
||||
* @param pxHigherPriorityTaskWoken It is possible that a message buffer will
|
||||
* have a task blocked on it waiting for space to become available. Calling
|
||||
* xMessageBufferReceiveFromISR() can make space available, and so cause a task
|
||||
* that is waiting for space to leave the Blocked state. If calling
|
||||
* xMessageBufferReceiveFromISR() causes a task to leave the Blocked state, and
|
||||
* the unblocked task has a priority higher than the currently executing task
|
||||
* (the task that was interrupted), then, internally,
|
||||
* xMessageBufferReceiveFromISR() will set *pxHigherPriorityTaskWoken to pdTRUE.
|
||||
* If xMessageBufferReceiveFromISR() sets this value to pdTRUE, then normally a
|
||||
* context switch should be performed before the interrupt is exited. That will
|
||||
* ensure the interrupt returns directly to the highest priority Ready state
|
||||
* task. *pxHigherPriorityTaskWoken should be set to pdFALSE before it is
|
||||
* passed into the function. See the code example below for an example.
|
||||
*
|
||||
* @return The length, in bytes, of the message read from the message buffer, if
|
||||
* any.
|
||||
*
|
||||
* Example use:
|
||||
* @code{c}
|
||||
* // A message buffer that has already been created.
|
||||
* MessageBuffer_t xMessageBuffer;
|
||||
*
|
||||
* void vAnInterruptServiceRoutine( void )
|
||||
* {
|
||||
* uint8_t ucRxData[ 20 ];
|
||||
* size_t xReceivedBytes;
|
||||
* BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
|
||||
*
|
||||
* // Receive the next message from the message buffer.
|
||||
* xReceivedBytes = xMessageBufferReceiveFromISR( xMessageBuffer,
|
||||
* ( void * ) ucRxData,
|
||||
* sizeof( ucRxData ),
|
||||
* &xHigherPriorityTaskWoken );
|
||||
*
|
||||
* if( xReceivedBytes > 0 )
|
||||
* {
|
||||
* // A ucRxData contains a message that is xReceivedBytes long. Process
|
||||
* // the message here....
|
||||
* }
|
||||
*
|
||||
* // If xHigherPriorityTaskWoken was set to pdTRUE inside
|
||||
* // xMessageBufferReceiveFromISR() then a task that has a priority above the
|
||||
* // priority of the currently executing task was unblocked and a context
|
||||
* // switch should be performed to ensure the ISR returns to the unblocked
|
||||
* // task. In most FreeRTOS ports this is done by simply passing
|
||||
* // xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the
|
||||
* // variables value, and perform the context switch if necessary. Check the
|
||||
* // documentation for the port in use for port specific instructions.
|
||||
* portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||
* }
|
||||
* @endcode
|
||||
* \defgroup xMessageBufferReceiveFromISR xMessageBufferReceiveFromISR
|
||||
* \ingroup MessageBufferManagement
|
||||
*/
|
||||
#define xMessageBufferReceiveFromISR( xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) \
|
||||
xStreamBufferReceiveFromISR( ( xMessageBuffer ), ( pvRxData ), ( xBufferLengthBytes ), ( pxHigherPriorityTaskWoken ) )
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
*
|
||||
* @code{c}
|
||||
* void vMessageBufferDelete( MessageBufferHandle_t xMessageBuffer );
|
||||
* @endcode
|
||||
*
|
||||
* Deletes a message buffer that was previously created using a call to
|
||||
* xMessageBufferCreate() or xMessageBufferCreateStatic(). If the message
|
||||
* buffer was created using dynamic memory (that is, by xMessageBufferCreate()),
|
||||
* then the allocated memory is freed.
|
||||
*
|
||||
* A message buffer handle must not be used after the message buffer has been
|
||||
* deleted.
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* vMessageBufferDelete() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the message buffer to be deleted.
|
||||
*
|
||||
*/
|
||||
#define vMessageBufferDelete( xMessageBuffer ) \
|
||||
vStreamBufferDelete( xMessageBuffer )
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
* @code{c}
|
||||
* BaseType_t xMessageBufferIsFull( MessageBufferHandle_t xMessageBuffer );
|
||||
* @endcode
|
||||
*
|
||||
* Tests to see if a message buffer is full. A message buffer is full if it
|
||||
* cannot accept any more messages, of any size, until space is made available
|
||||
* by a message being removed from the message buffer.
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferIsFull() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the message buffer being queried.
|
||||
*
|
||||
* @return If the message buffer referenced by xMessageBuffer is full then
|
||||
* pdTRUE is returned. Otherwise pdFALSE is returned.
|
||||
*/
|
||||
#define xMessageBufferIsFull( xMessageBuffer ) \
|
||||
xStreamBufferIsFull( xMessageBuffer )
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
* @code{c}
|
||||
* BaseType_t xMessageBufferIsEmpty( MessageBufferHandle_t xMessageBuffer );
|
||||
* @endcode
|
||||
*
|
||||
* Tests to see if a message buffer is empty (does not contain any messages).
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferIsEmpty() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the message buffer being queried.
|
||||
*
|
||||
* @return If the message buffer referenced by xMessageBuffer is empty then
|
||||
* pdTRUE is returned. Otherwise pdFALSE is returned.
|
||||
*
|
||||
*/
|
||||
#define xMessageBufferIsEmpty( xMessageBuffer ) \
|
||||
xStreamBufferIsEmpty( xMessageBuffer )
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
* @code{c}
|
||||
* BaseType_t xMessageBufferReset( MessageBufferHandle_t xMessageBuffer );
|
||||
* @endcode
|
||||
*
|
||||
* Resets a message buffer to its initial empty state, discarding any message it
|
||||
* contained.
|
||||
*
|
||||
* A message buffer can only be reset if there are no tasks blocked on it.
|
||||
*
|
||||
* Use xMessageBufferReset() to reset a message buffer from a task.
|
||||
* Use xMessageBufferResetFromISR() to reset a message buffer from an
|
||||
* interrupt service routine (ISR).
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferReset() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the message buffer being reset.
|
||||
*
|
||||
* @return If the message buffer was reset then pdPASS is returned. If the
|
||||
* message buffer could not be reset because either there was a task blocked on
|
||||
* the message queue to wait for space to become available, or to wait for a
|
||||
* a message to be available, then pdFAIL is returned.
|
||||
*
|
||||
* \defgroup xMessageBufferReset xMessageBufferReset
|
||||
* \ingroup MessageBufferManagement
|
||||
*/
|
||||
#define xMessageBufferReset( xMessageBuffer ) \
|
||||
xStreamBufferReset( xMessageBuffer )
|
||||
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
* @code{c}
|
||||
* BaseType_t xMessageBufferResetFromISR( MessageBufferHandle_t xMessageBuffer );
|
||||
* @endcode
|
||||
*
|
||||
* An interrupt safe version of the API function that resets the message buffer.
|
||||
* Resets a message buffer to its initial empty state, discarding any message it
|
||||
* contained.
|
||||
*
|
||||
* A message buffer can only be reset if there are no tasks blocked on it.
|
||||
*
|
||||
* Use xMessageBufferReset() to reset a message buffer from a task.
|
||||
* Use xMessageBufferResetFromISR() to reset a message buffer from an
|
||||
* interrupt service routine (ISR).
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferResetFromISR() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the message buffer being reset.
|
||||
*
|
||||
* @return If the message buffer was reset then pdPASS is returned. If the
|
||||
* message buffer could not be reset because either there was a task blocked on
|
||||
* the message queue to wait for space to become available, or to wait for a
|
||||
* a message to be available, then pdFAIL is returned.
|
||||
*
|
||||
* \defgroup xMessageBufferResetFromISR xMessageBufferResetFromISR
|
||||
* \ingroup MessageBufferManagement
|
||||
*/
|
||||
#define xMessageBufferResetFromISR( xMessageBuffer ) \
|
||||
xStreamBufferResetFromISR( xMessageBuffer )
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
* @code{c}
|
||||
* size_t xMessageBufferSpaceAvailable( MessageBufferHandle_t xMessageBuffer );
|
||||
* @endcode
|
||||
* Returns the number of bytes of free space in the message buffer.
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferSpaceAvailable() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the message buffer being queried.
|
||||
*
|
||||
* @return The number of bytes that can be written to the message buffer before
|
||||
* the message buffer would be full. When a message is written to the message
|
||||
* buffer an additional sizeof( size_t ) bytes are also written to store the
|
||||
* message's length. sizeof( size_t ) is typically 4 bytes on a 32-bit
|
||||
* architecture, so if xMessageBufferSpacesAvailable() returns 10, then the size
|
||||
* of the largest message that can be written to the message buffer is 6 bytes.
|
||||
*
|
||||
* \defgroup xMessageBufferSpaceAvailable xMessageBufferSpaceAvailable
|
||||
* \ingroup MessageBufferManagement
|
||||
*/
|
||||
#define xMessageBufferSpaceAvailable( xMessageBuffer ) \
|
||||
xStreamBufferSpacesAvailable( xMessageBuffer )
|
||||
#define xMessageBufferSpacesAvailable( xMessageBuffer ) \
|
||||
xStreamBufferSpacesAvailable( xMessageBuffer ) /* Corrects typo in original macro name. */
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
* @code{c}
|
||||
* size_t xMessageBufferNextLengthBytes( MessageBufferHandle_t xMessageBuffer );
|
||||
* @endcode
|
||||
* Returns the length (in bytes) of the next message in a message buffer.
|
||||
* Useful if xMessageBufferReceive() returned 0 because the size of the buffer
|
||||
* passed into xMessageBufferReceive() was too small to hold the next message.
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferNextLengthBytes() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the message buffer being queried.
|
||||
*
|
||||
* @return The length (in bytes) of the next message in the message buffer, or 0
|
||||
* if the message buffer is empty.
|
||||
*
|
||||
* \defgroup xMessageBufferNextLengthBytes xMessageBufferNextLengthBytes
|
||||
* \ingroup MessageBufferManagement
|
||||
*/
|
||||
#define xMessageBufferNextLengthBytes( xMessageBuffer ) \
|
||||
xStreamBufferNextMessageLengthBytes( xMessageBuffer )
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
*
|
||||
* @code{c}
|
||||
* BaseType_t xMessageBufferSendCompletedFromISR( MessageBufferHandle_t xMessageBuffer, BaseType_t *pxHigherPriorityTaskWoken );
|
||||
* @endcode
|
||||
*
|
||||
* For advanced users only.
|
||||
*
|
||||
* The sbSEND_COMPLETED() macro is called from within the FreeRTOS APIs when
|
||||
* data is sent to a message buffer or stream buffer. If there was a task that
|
||||
* was blocked on the message or stream buffer waiting for data to arrive then
|
||||
* the sbSEND_COMPLETED() macro sends a notification to the task to remove it
|
||||
* from the Blocked state. xMessageBufferSendCompletedFromISR() does the same
|
||||
* thing. It is provided to enable application writers to implement their own
|
||||
* version of sbSEND_COMPLETED(), and MUST NOT BE USED AT ANY OTHER TIME.
|
||||
*
|
||||
* See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for
|
||||
* additional information.
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferSendCompletedFromISR() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the stream buffer to which data was
|
||||
* written.
|
||||
*
|
||||
* @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be
|
||||
* initialised to pdFALSE before it is passed into
|
||||
* xMessageBufferSendCompletedFromISR(). If calling
|
||||
* xMessageBufferSendCompletedFromISR() removes a task from the Blocked state,
|
||||
* and the task has a priority above the priority of the currently running task,
|
||||
* then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a
|
||||
* context switch should be performed before exiting the ISR.
|
||||
*
|
||||
* @return If a task was removed from the Blocked state then pdTRUE is returned.
|
||||
* Otherwise pdFALSE is returned.
|
||||
*
|
||||
* \defgroup xMessageBufferSendCompletedFromISR xMessageBufferSendCompletedFromISR
|
||||
* \ingroup StreamBufferManagement
|
||||
*/
|
||||
#define xMessageBufferSendCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) \
|
||||
xStreamBufferSendCompletedFromISR( ( xMessageBuffer ), ( pxHigherPriorityTaskWoken ) )
|
||||
|
||||
/**
|
||||
* message_buffer.h
|
||||
*
|
||||
* @code{c}
|
||||
* BaseType_t xMessageBufferReceiveCompletedFromISR( MessageBufferHandle_t xMessageBuffer, BaseType_t *pxHigherPriorityTaskWoken );
|
||||
* @endcode
|
||||
*
|
||||
* For advanced users only.
|
||||
*
|
||||
* The sbRECEIVE_COMPLETED() macro is called from within the FreeRTOS APIs when
|
||||
* data is read out of a message buffer or stream buffer. If there was a task
|
||||
* that was blocked on the message or stream buffer waiting for data to arrive
|
||||
* then the sbRECEIVE_COMPLETED() macro sends a notification to the task to
|
||||
* remove it from the Blocked state. xMessageBufferReceiveCompletedFromISR()
|
||||
* does the same thing. It is provided to enable application writers to
|
||||
* implement their own version of sbRECEIVE_COMPLETED(), and MUST NOT BE USED AT
|
||||
* ANY OTHER TIME.
|
||||
*
|
||||
* See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for
|
||||
* additional information.
|
||||
*
|
||||
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||
* xMessageBufferReceiveCompletedFromISR() to be available.
|
||||
*
|
||||
* @param xMessageBuffer The handle of the stream buffer from which data was
|
||||
* read.
|
||||
*
|
||||
* @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be
|
||||
* initialised to pdFALSE before it is passed into
|
||||
* xMessageBufferReceiveCompletedFromISR(). If calling
|
||||
* xMessageBufferReceiveCompletedFromISR() removes a task from the Blocked state,
|
||||
* and the task has a priority above the priority of the currently running task,
|
||||
* then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a
|
||||
* context switch should be performed before exiting the ISR.
|
||||
*
|
||||
* @return If a task was removed from the Blocked state then pdTRUE is returned.
|
||||
* Otherwise pdFALSE is returned.
|
||||
*
|
||||
* \defgroup xMessageBufferReceiveCompletedFromISR xMessageBufferReceiveCompletedFromISR
|
||||
* \ingroup StreamBufferManagement
|
||||
*/
|
||||
#define xMessageBufferReceiveCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) \
|
||||
xStreamBufferReceiveCompletedFromISR( ( xMessageBuffer ), ( pxHigherPriorityTaskWoken ) )
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#if defined( __cplusplus )
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
#endif /* !defined( FREERTOS_MESSAGE_BUFFER_H ) */
|
||||
+495
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* When the MPU is used the standard (non MPU) API functions are mapped to
|
||||
* equivalents that start "MPU_", the prototypes for which are defined in this
|
||||
* header files. This will cause the application code to call the MPU_ version
|
||||
* which wraps the non-MPU version with privilege promoting then demoting code,
|
||||
* so the kernel code always runs will full privileges.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MPU_PROTOTYPES_H
|
||||
#define MPU_PROTOTYPES_H
|
||||
|
||||
typedef struct xTaskGenericNotifyParams
|
||||
{
|
||||
TaskHandle_t xTaskToNotify;
|
||||
UBaseType_t uxIndexToNotify;
|
||||
uint32_t ulValue;
|
||||
eNotifyAction eAction;
|
||||
uint32_t * pulPreviousNotificationValue;
|
||||
} xTaskGenericNotifyParams_t;
|
||||
|
||||
typedef struct xTaskGenericNotifyWaitParams
|
||||
{
|
||||
UBaseType_t uxIndexToWaitOn;
|
||||
uint32_t ulBitsToClearOnEntry;
|
||||
uint32_t ulBitsToClearOnExit;
|
||||
uint32_t * pulNotificationValue;
|
||||
TickType_t xTicksToWait;
|
||||
} xTaskGenericNotifyWaitParams_t;
|
||||
|
||||
typedef struct xTimerGenericCommandFromTaskParams
|
||||
{
|
||||
TimerHandle_t xTimer;
|
||||
BaseType_t xCommandID;
|
||||
TickType_t xOptionalValue;
|
||||
BaseType_t * pxHigherPriorityTaskWoken;
|
||||
TickType_t xTicksToWait;
|
||||
} xTimerGenericCommandFromTaskParams_t;
|
||||
|
||||
typedef struct xEventGroupWaitBitsParams
|
||||
{
|
||||
EventGroupHandle_t xEventGroup;
|
||||
EventBits_t uxBitsToWaitFor;
|
||||
BaseType_t xClearOnExit;
|
||||
BaseType_t xWaitForAllBits;
|
||||
TickType_t xTicksToWait;
|
||||
} xEventGroupWaitBitsParams_t;
|
||||
|
||||
/* MPU versions of task.h API functions. */
|
||||
void MPU_vTaskDelay( const TickType_t xTicksToDelay ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
|
||||
const TickType_t xTimeIncrement ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTaskAbortDelay( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||
UBaseType_t MPU_uxTaskPriorityGet( const TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||
eTaskState MPU_eTaskGetState( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTaskGetInfo( TaskHandle_t xTask,
|
||||
TaskStatus_t * pxTaskStatus,
|
||||
BaseType_t xGetFreeStackSpace,
|
||||
eTaskState eState ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTaskSuspend( TaskHandle_t xTaskToSuspend ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTaskResume( TaskHandle_t xTaskToResume ) FREERTOS_SYSTEM_CALL;
|
||||
TickType_t MPU_xTaskGetTickCount( void ) FREERTOS_SYSTEM_CALL;
|
||||
UBaseType_t MPU_uxTaskGetNumberOfTasks( void ) FREERTOS_SYSTEM_CALL;
|
||||
UBaseType_t MPU_uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||
configSTACK_DEPTH_TYPE MPU_uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTaskSetApplicationTaskTag( TaskHandle_t xTask,
|
||||
TaskHookFunction_t pxHookFunction ) FREERTOS_SYSTEM_CALL;
|
||||
TaskHookFunction_t MPU_xTaskGetApplicationTaskTag( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet,
|
||||
BaseType_t xIndex,
|
||||
void * pvValue ) FREERTOS_SYSTEM_CALL;
|
||||
void * MPU_pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery,
|
||||
BaseType_t xIndex ) FREERTOS_SYSTEM_CALL;
|
||||
TaskHandle_t MPU_xTaskGetIdleTaskHandle( void ) FREERTOS_SYSTEM_CALL;
|
||||
UBaseType_t MPU_uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray,
|
||||
const UBaseType_t uxArraySize,
|
||||
configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime ) FREERTOS_SYSTEM_CALL;
|
||||
configRUN_TIME_COUNTER_TYPE MPU_ulTaskGetRunTimeCounter( const TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||
configRUN_TIME_COUNTER_TYPE MPU_ulTaskGetRunTimePercent( const TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||
configRUN_TIME_COUNTER_TYPE MPU_ulTaskGetIdleRunTimeCounter( void ) FREERTOS_SYSTEM_CALL;
|
||||
configRUN_TIME_COUNTER_TYPE MPU_ulTaskGetIdleRunTimePercent( void ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTaskGenericNotify( TaskHandle_t xTaskToNotify,
|
||||
UBaseType_t uxIndexToNotify,
|
||||
uint32_t ulValue,
|
||||
eNotifyAction eAction,
|
||||
uint32_t * pulPreviousNotificationValue ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTaskGenericNotifyEntry( const xTaskGenericNotifyParams_t * pxParams ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn,
|
||||
uint32_t ulBitsToClearOnEntry,
|
||||
uint32_t ulBitsToClearOnExit,
|
||||
uint32_t * pulNotificationValue,
|
||||
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTaskGenericNotifyWaitEntry( const xTaskGenericNotifyWaitParams_t * pxParams ) FREERTOS_SYSTEM_CALL;
|
||||
uint32_t MPU_ulTaskGenericNotifyTake( UBaseType_t uxIndexToWaitOn,
|
||||
BaseType_t xClearCountOnExit,
|
||||
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTaskGenericNotifyStateClear( TaskHandle_t xTask,
|
||||
UBaseType_t uxIndexToClear ) FREERTOS_SYSTEM_CALL;
|
||||
uint32_t MPU_ulTaskGenericNotifyValueClear( TaskHandle_t xTask,
|
||||
UBaseType_t uxIndexToClear,
|
||||
uint32_t ulBitsToClear ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut,
|
||||
TickType_t * const pxTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
TaskHandle_t MPU_xTaskGetCurrentTaskHandle( void ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTaskGetSchedulerState( void ) FREERTOS_SYSTEM_CALL;
|
||||
|
||||
/* Privileged only wrappers for Task APIs. These are needed so that
|
||||
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||
* with all the APIs. */
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 1 )
|
||||
|
||||
BaseType_t MPU_xTaskCreate( TaskFunction_t pxTaskCode,
|
||||
const char * const pcName,
|
||||
const configSTACK_DEPTH_TYPE uxStackDepth,
|
||||
void * const pvParameters,
|
||||
UBaseType_t uxPriority,
|
||||
TaskHandle_t * const pxCreatedTask ) FREERTOS_SYSTEM_CALL;
|
||||
TaskHandle_t MPU_xTaskCreateStatic( TaskFunction_t pxTaskCode,
|
||||
const char * const pcName,
|
||||
const configSTACK_DEPTH_TYPE uxStackDepth,
|
||||
void * const pvParameters,
|
||||
UBaseType_t uxPriority,
|
||||
StackType_t * const puxStackBuffer,
|
||||
StaticTask_t * const pxTaskBuffer ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTaskDelete( TaskHandle_t xTaskToDelete ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTaskPrioritySet( TaskHandle_t xTask,
|
||||
UBaseType_t uxNewPriority ) FREERTOS_SYSTEM_CALL;
|
||||
TaskHandle_t MPU_xTaskGetHandle( const char * pcNameToQuery ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask,
|
||||
void * pvParameter ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTaskGetRunTimeStatistics( char * pcWriteBuffer,
|
||||
size_t uxBufferLength ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTaskListTasks( char * pcWriteBuffer,
|
||||
size_t uxBufferLength ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTaskSuspendAll( void ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTaskResumeAll( void ) FREERTOS_SYSTEM_CALL;
|
||||
|
||||
#else /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||
|
||||
BaseType_t MPU_xTaskCreate( TaskFunction_t pxTaskCode,
|
||||
const char * const pcName,
|
||||
const configSTACK_DEPTH_TYPE uxStackDepth,
|
||||
void * const pvParameters,
|
||||
UBaseType_t uxPriority,
|
||||
TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION;
|
||||
TaskHandle_t MPU_xTaskCreateStatic( TaskFunction_t pxTaskCode,
|
||||
const char * const pcName,
|
||||
const configSTACK_DEPTH_TYPE uxStackDepth,
|
||||
void * const pvParameters,
|
||||
UBaseType_t uxPriority,
|
||||
StackType_t * const puxStackBuffer,
|
||||
StaticTask_t * const pxTaskBuffer ) PRIVILEGED_FUNCTION;
|
||||
void MPU_vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION;
|
||||
void MPU_vTaskPrioritySet( TaskHandle_t xTask,
|
||||
UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION;
|
||||
TaskHandle_t MPU_xTaskGetHandle( const char * pcNameToQuery ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask,
|
||||
void * pvParameter ) PRIVILEGED_FUNCTION;
|
||||
|
||||
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||
|
||||
char * MPU_pcTaskGetName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition,
|
||||
TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition,
|
||||
TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION;
|
||||
void MPU_vTaskAllocateMPURegions( TaskHandle_t xTaskToModify,
|
||||
const MemoryRegion_t * const xRegions ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xTaskGetStaticBuffers( TaskHandle_t xTask,
|
||||
StackType_t ** ppuxStackBuffer,
|
||||
StaticTask_t ** ppxTaskBuffer ) PRIVILEGED_FUNCTION;
|
||||
UBaseType_t MPU_uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION;
|
||||
UBaseType_t MPU_uxTaskBasePriorityGet( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION;
|
||||
UBaseType_t MPU_uxTaskBasePriorityGetFromISR( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION;
|
||||
TaskHookFunction_t MPU_xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify,
|
||||
UBaseType_t uxIndexToNotify,
|
||||
uint32_t ulValue,
|
||||
eNotifyAction eAction,
|
||||
uint32_t * pulPreviousNotificationValue,
|
||||
BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||
void MPU_vTaskGenericNotifyGiveFromISR( TaskHandle_t xTaskToNotify,
|
||||
UBaseType_t uxIndexToNotify,
|
||||
BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/* MPU versions of queue.h API functions. */
|
||||
BaseType_t MPU_xQueueGenericSend( QueueHandle_t xQueue,
|
||||
const void * const pvItemToQueue,
|
||||
TickType_t xTicksToWait,
|
||||
const BaseType_t xCopyPosition ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xQueueReceive( QueueHandle_t xQueue,
|
||||
void * const pvBuffer,
|
||||
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xQueuePeek( QueueHandle_t xQueue,
|
||||
void * const pvBuffer,
|
||||
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xQueueSemaphoreTake( QueueHandle_t xQueue,
|
||||
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
UBaseType_t MPU_uxQueueMessagesWaiting( const QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||
UBaseType_t MPU_uxQueueSpacesAvailable( const QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||
TaskHandle_t MPU_xQueueGetMutexHolder( QueueHandle_t xSemaphore ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xQueueTakeMutexRecursive( QueueHandle_t xMutex,
|
||||
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vQueueAddToRegistry( QueueHandle_t xQueue,
|
||||
const char * pcName ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vQueueUnregisterQueue( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||
const char * MPU_pcQueueGetName( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,
|
||||
QueueSetHandle_t xQueueSet ) FREERTOS_SYSTEM_CALL;
|
||||
QueueSetMemberHandle_t MPU_xQueueSelectFromSet( QueueSetHandle_t xQueueSet,
|
||||
const TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vQueueSetQueueNumber( QueueHandle_t xQueue,
|
||||
UBaseType_t uxQueueNumber ) FREERTOS_SYSTEM_CALL;
|
||||
UBaseType_t MPU_uxQueueGetQueueNumber( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||
uint8_t MPU_ucQueueGetQueueType( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||
|
||||
/* Privileged only wrappers for Queue APIs. These are needed so that
|
||||
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||
* with all the APIs. */
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 1 )
|
||||
|
||||
void MPU_vQueueDelete( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||
QueueHandle_t MPU_xQueueCreateMutex( const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL;
|
||||
QueueHandle_t MPU_xQueueCreateMutexStatic( const uint8_t ucQueueType,
|
||||
StaticQueue_t * pxStaticQueue ) FREERTOS_SYSTEM_CALL;
|
||||
QueueHandle_t MPU_xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount,
|
||||
const UBaseType_t uxInitialCount ) FREERTOS_SYSTEM_CALL;
|
||||
QueueHandle_t MPU_xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount,
|
||||
const UBaseType_t uxInitialCount,
|
||||
StaticQueue_t * pxStaticQueue ) FREERTOS_SYSTEM_CALL;
|
||||
QueueHandle_t MPU_xQueueGenericCreate( const UBaseType_t uxQueueLength,
|
||||
const UBaseType_t uxItemSize,
|
||||
const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL;
|
||||
QueueHandle_t MPU_xQueueGenericCreateStatic( const UBaseType_t uxQueueLength,
|
||||
const UBaseType_t uxItemSize,
|
||||
uint8_t * pucQueueStorage,
|
||||
StaticQueue_t * pxStaticQueue,
|
||||
const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL;
|
||||
QueueSetHandle_t MPU_xQueueCreateSet( const UBaseType_t uxEventQueueLength ) FREERTOS_SYSTEM_CALL;
|
||||
QueueSetHandle_t MPU_xQueueCreateSetStatic( const UBaseType_t uxEventQueueLength,
|
||||
uint8_t * pucQueueStorage,
|
||||
StaticQueue_t * pxStaticQueue ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore,
|
||||
QueueSetHandle_t xQueueSet ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xQueueGenericReset( QueueHandle_t xQueue,
|
||||
BaseType_t xNewQueue ) FREERTOS_SYSTEM_CALL;
|
||||
|
||||
#else /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||
|
||||
void MPU_vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
|
||||
QueueHandle_t MPU_xQueueCreateMutex( const uint8_t ucQueueType ) PRIVILEGED_FUNCTION;
|
||||
QueueHandle_t MPU_xQueueCreateMutexStatic( const uint8_t ucQueueType,
|
||||
StaticQueue_t * pxStaticQueue ) PRIVILEGED_FUNCTION;
|
||||
QueueHandle_t MPU_xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount,
|
||||
const UBaseType_t uxInitialCount ) PRIVILEGED_FUNCTION;
|
||||
QueueHandle_t MPU_xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount,
|
||||
const UBaseType_t uxInitialCount,
|
||||
StaticQueue_t * pxStaticQueue ) PRIVILEGED_FUNCTION;
|
||||
QueueHandle_t MPU_xQueueGenericCreate( const UBaseType_t uxQueueLength,
|
||||
const UBaseType_t uxItemSize,
|
||||
const uint8_t ucQueueType ) PRIVILEGED_FUNCTION;
|
||||
QueueHandle_t MPU_xQueueGenericCreateStatic( const UBaseType_t uxQueueLength,
|
||||
const UBaseType_t uxItemSize,
|
||||
uint8_t * pucQueueStorage,
|
||||
StaticQueue_t * pxStaticQueue,
|
||||
const uint8_t ucQueueType ) PRIVILEGED_FUNCTION;
|
||||
QueueSetHandle_t MPU_xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILEGED_FUNCTION;
|
||||
QueueSetHandle_t MPU_xQueueCreateSetStatic( const UBaseType_t uxEventQueueLength,
|
||||
uint8_t * pucQueueStorage,
|
||||
StaticQueue_t * pxStaticQueue ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore,
|
||||
QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xQueueGenericReset( QueueHandle_t xQueue,
|
||||
BaseType_t xNewQueue ) PRIVILEGED_FUNCTION;
|
||||
|
||||
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||
|
||||
BaseType_t MPU_xQueueGenericGetStaticBuffers( QueueHandle_t xQueue,
|
||||
uint8_t ** ppucQueueStorage,
|
||||
StaticQueue_t ** ppxStaticQueue ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xQueueGenericSendFromISR( QueueHandle_t xQueue,
|
||||
const void * const pvItemToQueue,
|
||||
BaseType_t * const pxHigherPriorityTaskWoken,
|
||||
const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xQueueGiveFromISR( QueueHandle_t xQueue,
|
||||
BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xQueuePeekFromISR( QueueHandle_t xQueue,
|
||||
void * const pvBuffer ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xQueueReceiveFromISR( QueueHandle_t xQueue,
|
||||
void * const pvBuffer,
|
||||
BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
|
||||
UBaseType_t MPU_uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
|
||||
TaskHandle_t MPU_xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION;
|
||||
QueueSetMemberHandle_t MPU_xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/* MPU versions of timers.h API functions. */
|
||||
void * MPU_pvTimerGetTimerID( const TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTimerSetTimerID( TimerHandle_t xTimer,
|
||||
void * pvNewID ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTimerIsTimerActive( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||
TaskHandle_t MPU_xTimerGetTimerDaemonTaskHandle( void ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTimerGenericCommandFromTask( TimerHandle_t xTimer,
|
||||
const BaseType_t xCommandID,
|
||||
const TickType_t xOptionalValue,
|
||||
BaseType_t * const pxHigherPriorityTaskWoken,
|
||||
const TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTimerGenericCommandFromTaskEntry( const xTimerGenericCommandFromTaskParams_t * pxParams ) FREERTOS_SYSTEM_CALL;
|
||||
const char * MPU_pcTimerGetName( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vTimerSetReloadMode( TimerHandle_t xTimer,
|
||||
const BaseType_t xAutoReload ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xTimerGetReloadMode( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||
UBaseType_t MPU_uxTimerGetReloadMode( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||
TickType_t MPU_xTimerGetPeriod( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||
TickType_t MPU_xTimerGetExpiryTime( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||
|
||||
/* Privileged only wrappers for Timer APIs. These are needed so that
|
||||
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||
* with all the APIs. */
|
||||
TimerHandle_t MPU_xTimerCreate( const char * const pcTimerName,
|
||||
const TickType_t xTimerPeriodInTicks,
|
||||
const BaseType_t xAutoReload,
|
||||
void * const pvTimerID,
|
||||
TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION;
|
||||
TimerHandle_t MPU_xTimerCreateStatic( const char * const pcTimerName,
|
||||
const TickType_t xTimerPeriodInTicks,
|
||||
const BaseType_t xAutoReload,
|
||||
void * const pvTimerID,
|
||||
TimerCallbackFunction_t pxCallbackFunction,
|
||||
StaticTimer_t * pxTimerBuffer ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xTimerGetStaticBuffer( TimerHandle_t xTimer,
|
||||
StaticTimer_t ** ppxTimerBuffer ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xTimerGenericCommandFromISR( TimerHandle_t xTimer,
|
||||
const BaseType_t xCommandID,
|
||||
const TickType_t xOptionalValue,
|
||||
BaseType_t * const pxHigherPriorityTaskWoken,
|
||||
const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/* MPU versions of event_group.h API functions. */
|
||||
EventBits_t MPU_xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToWaitFor,
|
||||
const BaseType_t xClearOnExit,
|
||||
const BaseType_t xWaitForAllBits,
|
||||
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
EventBits_t MPU_xEventGroupWaitBitsEntry( const xEventGroupWaitBitsParams_t * pxParams ) FREERTOS_SYSTEM_CALL;
|
||||
EventBits_t MPU_xEventGroupClearBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToClear ) FREERTOS_SYSTEM_CALL;
|
||||
EventBits_t MPU_xEventGroupSetBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToSet ) FREERTOS_SYSTEM_CALL;
|
||||
EventBits_t MPU_xEventGroupSync( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToSet,
|
||||
const EventBits_t uxBitsToWaitFor,
|
||||
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
#if ( configUSE_TRACE_FACILITY == 1 )
|
||||
UBaseType_t MPU_uxEventGroupGetNumber( void * xEventGroup ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vEventGroupSetNumber( void * xEventGroup,
|
||||
UBaseType_t uxEventGroupNumber ) FREERTOS_SYSTEM_CALL;
|
||||
#endif /* #if ( configUSE_TRACE_FACILITY == 1 ) */
|
||||
|
||||
/* Privileged only wrappers for Event Group APIs. These are needed so that
|
||||
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||
* with all the APIs. */
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 1 )
|
||||
|
||||
EventGroupHandle_t MPU_xEventGroupCreate( void ) FREERTOS_SYSTEM_CALL;
|
||||
EventGroupHandle_t MPU_xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vEventGroupDelete( EventGroupHandle_t xEventGroup ) FREERTOS_SYSTEM_CALL;
|
||||
|
||||
#else /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||
|
||||
EventGroupHandle_t MPU_xEventGroupCreate( void ) PRIVILEGED_FUNCTION;
|
||||
EventGroupHandle_t MPU_xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer ) PRIVILEGED_FUNCTION;
|
||||
void MPU_vEventGroupDelete( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION;
|
||||
|
||||
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||
|
||||
BaseType_t MPU_xEventGroupGetStaticBuffer( EventGroupHandle_t xEventGroup,
|
||||
StaticEventGroup_t ** ppxEventGroupBuffer ) PRIVILEGED_FUNCTION;
|
||||
EventBits_t MPU_xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION;
|
||||
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||
BaseType_t MPU_xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToClear ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToSet,
|
||||
BaseType_t * pxHigherPriorityTaskWoken ) FREERTOS_SYSTEM_CALL;
|
||||
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||
|
||||
/* MPU versions of message/stream_buffer.h API functions. */
|
||||
size_t MPU_xStreamBufferSend( StreamBufferHandle_t xStreamBuffer,
|
||||
const void * pvTxData,
|
||||
size_t xDataLengthBytes,
|
||||
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
size_t MPU_xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer,
|
||||
void * pvRxData,
|
||||
size_t xBufferLengthBytes,
|
||||
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||
size_t MPU_xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||
size_t MPU_xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer,
|
||||
size_t xTriggerLevel ) FREERTOS_SYSTEM_CALL;
|
||||
size_t MPU_xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||
|
||||
/* Privileged only wrappers for Stream Buffer APIs. These are needed so that
|
||||
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||
* with all the APIs. */
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 1 )
|
||||
|
||||
StreamBufferHandle_t MPU_xStreamBufferGenericCreate( size_t xBufferSizeBytes,
|
||||
size_t xTriggerLevelBytes,
|
||||
BaseType_t xStreamBufferType,
|
||||
StreamBufferCallbackFunction_t pxSendCompletedCallback,
|
||||
StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) FREERTOS_SYSTEM_CALL;
|
||||
StreamBufferHandle_t MPU_xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes,
|
||||
size_t xTriggerLevelBytes,
|
||||
BaseType_t xStreamBufferType,
|
||||
uint8_t * const pucStreamBufferStorageArea,
|
||||
StaticStreamBuffer_t * const pxStaticStreamBuffer,
|
||||
StreamBufferCallbackFunction_t pxSendCompletedCallback,
|
||||
StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) FREERTOS_SYSTEM_CALL;
|
||||
void MPU_vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||
BaseType_t MPU_xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||
|
||||
#else /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||
|
||||
StreamBufferHandle_t MPU_xStreamBufferGenericCreate( size_t xBufferSizeBytes,
|
||||
size_t xTriggerLevelBytes,
|
||||
BaseType_t xStreamBufferType,
|
||||
StreamBufferCallbackFunction_t pxSendCompletedCallback,
|
||||
StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) PRIVILEGED_FUNCTION;
|
||||
StreamBufferHandle_t MPU_xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes,
|
||||
size_t xTriggerLevelBytes,
|
||||
BaseType_t xStreamBufferType,
|
||||
uint8_t * const pucStreamBufferStorageArea,
|
||||
StaticStreamBuffer_t * const pxStaticStreamBuffer,
|
||||
StreamBufferCallbackFunction_t pxSendCompletedCallback,
|
||||
StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) PRIVILEGED_FUNCTION;
|
||||
void MPU_vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION;
|
||||
|
||||
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||
|
||||
BaseType_t MPU_xStreamBufferGetStaticBuffers( StreamBufferHandle_t xStreamBuffers,
|
||||
uint8_t * ppucStreamBufferStorageArea,
|
||||
StaticStreamBuffer_t * ppxStaticStreamBuffer ) PRIVILEGED_FUNCTION;
|
||||
size_t MPU_xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer,
|
||||
const void * pvTxData,
|
||||
size_t xDataLengthBytes,
|
||||
BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||
size_t MPU_xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer,
|
||||
void * pvRxData,
|
||||
size_t xBufferLengthBytes,
|
||||
BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer,
|
||||
BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer,
|
||||
BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||
BaseType_t MPU_xStreamBufferResetFromISR( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION;
|
||||
|
||||
#endif /* MPU_PROTOTYPES_H */
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MPU_SYSCALL_NUMBERS_H
|
||||
#define MPU_SYSCALL_NUMBERS_H
|
||||
|
||||
/* Numbers assigned to various system calls. */
|
||||
#define SYSTEM_CALL_xTaskGenericNotify 0
|
||||
#define SYSTEM_CALL_xTaskGenericNotifyWait 1
|
||||
#define SYSTEM_CALL_xTimerGenericCommandFromTask 2
|
||||
#define SYSTEM_CALL_xEventGroupWaitBits 3
|
||||
#define SYSTEM_CALL_xTaskDelayUntil 4
|
||||
#define SYSTEM_CALL_xTaskAbortDelay 5
|
||||
#define SYSTEM_CALL_vTaskDelay 6
|
||||
#define SYSTEM_CALL_uxTaskPriorityGet 7
|
||||
#define SYSTEM_CALL_eTaskGetState 8
|
||||
#define SYSTEM_CALL_vTaskGetInfo 9
|
||||
#define SYSTEM_CALL_xTaskGetIdleTaskHandle 10
|
||||
#define SYSTEM_CALL_vTaskSuspend 11
|
||||
#define SYSTEM_CALL_vTaskResume 12
|
||||
#define SYSTEM_CALL_xTaskGetTickCount 13
|
||||
#define SYSTEM_CALL_uxTaskGetNumberOfTasks 14
|
||||
#define SYSTEM_CALL_ulTaskGetRunTimeCounter 15
|
||||
#define SYSTEM_CALL_ulTaskGetRunTimePercent 16
|
||||
#define SYSTEM_CALL_ulTaskGetIdleRunTimePercent 17
|
||||
#define SYSTEM_CALL_ulTaskGetIdleRunTimeCounter 18
|
||||
#define SYSTEM_CALL_vTaskSetApplicationTaskTag 19
|
||||
#define SYSTEM_CALL_xTaskGetApplicationTaskTag 20
|
||||
#define SYSTEM_CALL_vTaskSetThreadLocalStoragePointer 21
|
||||
#define SYSTEM_CALL_pvTaskGetThreadLocalStoragePointer 22
|
||||
#define SYSTEM_CALL_uxTaskGetSystemState 23
|
||||
#define SYSTEM_CALL_uxTaskGetStackHighWaterMark 24
|
||||
#define SYSTEM_CALL_uxTaskGetStackHighWaterMark2 25
|
||||
#define SYSTEM_CALL_xTaskGetCurrentTaskHandle 26
|
||||
#define SYSTEM_CALL_xTaskGetSchedulerState 27
|
||||
#define SYSTEM_CALL_vTaskSetTimeOutState 28
|
||||
#define SYSTEM_CALL_xTaskCheckForTimeOut 29
|
||||
#define SYSTEM_CALL_ulTaskGenericNotifyTake 30
|
||||
#define SYSTEM_CALL_xTaskGenericNotifyStateClear 31
|
||||
#define SYSTEM_CALL_ulTaskGenericNotifyValueClear 32
|
||||
#define SYSTEM_CALL_xQueueGenericSend 33
|
||||
#define SYSTEM_CALL_uxQueueMessagesWaiting 34
|
||||
#define SYSTEM_CALL_uxQueueSpacesAvailable 35
|
||||
#define SYSTEM_CALL_xQueueReceive 36
|
||||
#define SYSTEM_CALL_xQueuePeek 37
|
||||
#define SYSTEM_CALL_xQueueSemaphoreTake 38
|
||||
#define SYSTEM_CALL_xQueueGetMutexHolder 39
|
||||
#define SYSTEM_CALL_xQueueTakeMutexRecursive 40
|
||||
#define SYSTEM_CALL_xQueueGiveMutexRecursive 41
|
||||
#define SYSTEM_CALL_xQueueSelectFromSet 42
|
||||
#define SYSTEM_CALL_xQueueAddToSet 43
|
||||
#define SYSTEM_CALL_vQueueAddToRegistry 44
|
||||
#define SYSTEM_CALL_vQueueUnregisterQueue 45
|
||||
#define SYSTEM_CALL_pcQueueGetName 46
|
||||
#define SYSTEM_CALL_pvTimerGetTimerID 47
|
||||
#define SYSTEM_CALL_vTimerSetTimerID 48
|
||||
#define SYSTEM_CALL_xTimerIsTimerActive 49
|
||||
#define SYSTEM_CALL_xTimerGetTimerDaemonTaskHandle 50
|
||||
#define SYSTEM_CALL_pcTimerGetName 51
|
||||
#define SYSTEM_CALL_vTimerSetReloadMode 52
|
||||
#define SYSTEM_CALL_xTimerGetReloadMode 53
|
||||
#define SYSTEM_CALL_uxTimerGetReloadMode 54
|
||||
#define SYSTEM_CALL_xTimerGetPeriod 55
|
||||
#define SYSTEM_CALL_xTimerGetExpiryTime 56
|
||||
#define SYSTEM_CALL_xEventGroupClearBits 57
|
||||
#define SYSTEM_CALL_xEventGroupSetBits 58
|
||||
#define SYSTEM_CALL_xEventGroupSync 59
|
||||
#define SYSTEM_CALL_uxEventGroupGetNumber 60
|
||||
#define SYSTEM_CALL_vEventGroupSetNumber 61
|
||||
#define SYSTEM_CALL_xStreamBufferSend 62
|
||||
#define SYSTEM_CALL_xStreamBufferReceive 63
|
||||
#define SYSTEM_CALL_xStreamBufferIsFull 64
|
||||
#define SYSTEM_CALL_xStreamBufferIsEmpty 65
|
||||
#define SYSTEM_CALL_xStreamBufferSpacesAvailable 66
|
||||
#define SYSTEM_CALL_xStreamBufferBytesAvailable 67
|
||||
#define SYSTEM_CALL_xStreamBufferSetTriggerLevel 68
|
||||
#define SYSTEM_CALL_xStreamBufferNextMessageLengthBytes 69
|
||||
#define NUM_SYSTEM_CALLS 70 /* Total number of system calls. */
|
||||
|
||||
#endif /* MPU_SYSCALL_NUMBERS_H */
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MPU_WRAPPERS_H
|
||||
#define MPU_WRAPPERS_H
|
||||
|
||||
/* This file redefines API functions to be called through a wrapper macro, but
|
||||
* only for ports that are using the MPU. */
|
||||
#if ( portUSING_MPU_WRAPPERS == 1 )
|
||||
|
||||
/* MPU_WRAPPERS_INCLUDED_FROM_API_FILE will be defined when this file is
|
||||
* included from queue.c or task.c to prevent it from having an effect within
|
||||
* those files. */
|
||||
#ifndef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||
|
||||
/*
|
||||
* Map standard (non MPU) API functions to equivalents that start
|
||||
* "MPU_". This will cause the application code to call the MPU_
|
||||
* version, which wraps the non-MPU version with privilege promoting
|
||||
* then demoting code, so the kernel code always runs will full
|
||||
* privileges.
|
||||
*/
|
||||
|
||||
/* Map standard task.h API functions to the MPU equivalents. */
|
||||
#define vTaskDelay MPU_vTaskDelay
|
||||
#define xTaskDelayUntil MPU_xTaskDelayUntil
|
||||
#define xTaskAbortDelay MPU_xTaskAbortDelay
|
||||
#define uxTaskPriorityGet MPU_uxTaskPriorityGet
|
||||
#define eTaskGetState MPU_eTaskGetState
|
||||
#define vTaskGetInfo MPU_vTaskGetInfo
|
||||
#define vTaskSuspend MPU_vTaskSuspend
|
||||
#define vTaskResume MPU_vTaskResume
|
||||
#define xTaskGetTickCount MPU_xTaskGetTickCount
|
||||
#define uxTaskGetNumberOfTasks MPU_uxTaskGetNumberOfTasks
|
||||
#define uxTaskGetStackHighWaterMark MPU_uxTaskGetStackHighWaterMark
|
||||
#define uxTaskGetStackHighWaterMark2 MPU_uxTaskGetStackHighWaterMark2
|
||||
#define vTaskSetApplicationTaskTag MPU_vTaskSetApplicationTaskTag
|
||||
#define xTaskGetApplicationTaskTag MPU_xTaskGetApplicationTaskTag
|
||||
#define vTaskSetThreadLocalStoragePointer MPU_vTaskSetThreadLocalStoragePointer
|
||||
#define pvTaskGetThreadLocalStoragePointer MPU_pvTaskGetThreadLocalStoragePointer
|
||||
#define xTaskGetIdleTaskHandle MPU_xTaskGetIdleTaskHandle
|
||||
#define uxTaskGetSystemState MPU_uxTaskGetSystemState
|
||||
#define ulTaskGetIdleRunTimeCounter MPU_ulTaskGetIdleRunTimeCounter
|
||||
#define ulTaskGetIdleRunTimePercent MPU_ulTaskGetIdleRunTimePercent
|
||||
#define xTaskGenericNotify MPU_xTaskGenericNotify
|
||||
#define xTaskGenericNotifyWait MPU_xTaskGenericNotifyWait
|
||||
#define ulTaskGenericNotifyTake MPU_ulTaskGenericNotifyTake
|
||||
#define xTaskGenericNotifyStateClear MPU_xTaskGenericNotifyStateClear
|
||||
#define ulTaskGenericNotifyValueClear MPU_ulTaskGenericNotifyValueClear
|
||||
#define vTaskSetTimeOutState MPU_vTaskSetTimeOutState
|
||||
#define xTaskCheckForTimeOut MPU_xTaskCheckForTimeOut
|
||||
#define xTaskGetCurrentTaskHandle MPU_xTaskGetCurrentTaskHandle
|
||||
#define xTaskGetSchedulerState MPU_xTaskGetSchedulerState
|
||||
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||
#define ulTaskGetRunTimeCounter MPU_ulTaskGetRunTimeCounter
|
||||
#define ulTaskGetRunTimePercent MPU_ulTaskGetRunTimePercent
|
||||
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||
|
||||
/* Privileged only wrappers for Task APIs. These are needed so that
|
||||
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||
* with all the APIs. */
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 1 )
|
||||
|
||||
/* These are not needed in v2 because they do not take a task
|
||||
* handle and therefore, no lookup is needed. Needed in v1 because
|
||||
* these are available as system calls in v1. */
|
||||
#define vTaskGetRunTimeStatistics MPU_vTaskGetRunTimeStatistics
|
||||
#define vTaskListTasks MPU_vTaskListTasks
|
||||
#define vTaskSuspendAll MPU_vTaskSuspendAll
|
||||
#define xTaskCatchUpTicks MPU_xTaskCatchUpTicks
|
||||
#define xTaskResumeAll MPU_xTaskResumeAll
|
||||
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||
|
||||
#define xTaskCreate MPU_xTaskCreate
|
||||
#define xTaskCreateStatic MPU_xTaskCreateStatic
|
||||
#define vTaskDelete MPU_vTaskDelete
|
||||
#define vTaskPrioritySet MPU_vTaskPrioritySet
|
||||
#define xTaskGetHandle MPU_xTaskGetHandle
|
||||
#define xTaskCallApplicationTaskHook MPU_xTaskCallApplicationTaskHook
|
||||
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||
#define pcTaskGetName MPU_pcTaskGetName
|
||||
#define xTaskCreateRestricted MPU_xTaskCreateRestricted
|
||||
#define xTaskCreateRestrictedStatic MPU_xTaskCreateRestrictedStatic
|
||||
#define vTaskAllocateMPURegions MPU_vTaskAllocateMPURegions
|
||||
#define xTaskGetStaticBuffers MPU_xTaskGetStaticBuffers
|
||||
#define uxTaskPriorityGetFromISR MPU_uxTaskPriorityGetFromISR
|
||||
#define uxTaskBasePriorityGet MPU_uxTaskBasePriorityGet
|
||||
#define uxTaskBasePriorityGetFromISR MPU_uxTaskBasePriorityGetFromISR
|
||||
#define xTaskResumeFromISR MPU_xTaskResumeFromISR
|
||||
#define xTaskGetApplicationTaskTagFromISR MPU_xTaskGetApplicationTaskTagFromISR
|
||||
#define xTaskGenericNotifyFromISR MPU_xTaskGenericNotifyFromISR
|
||||
#define vTaskGenericNotifyGiveFromISR MPU_vTaskGenericNotifyGiveFromISR
|
||||
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||
|
||||
/* Map standard queue.h API functions to the MPU equivalents. */
|
||||
#define xQueueGenericSend MPU_xQueueGenericSend
|
||||
#define xQueueReceive MPU_xQueueReceive
|
||||
#define xQueuePeek MPU_xQueuePeek
|
||||
#define xQueueSemaphoreTake MPU_xQueueSemaphoreTake
|
||||
#define uxQueueMessagesWaiting MPU_uxQueueMessagesWaiting
|
||||
#define uxQueueSpacesAvailable MPU_uxQueueSpacesAvailable
|
||||
#define xQueueGetMutexHolder MPU_xQueueGetMutexHolder
|
||||
#define xQueueTakeMutexRecursive MPU_xQueueTakeMutexRecursive
|
||||
#define xQueueGiveMutexRecursive MPU_xQueueGiveMutexRecursive
|
||||
#define xQueueAddToSet MPU_xQueueAddToSet
|
||||
#define xQueueSelectFromSet MPU_xQueueSelectFromSet
|
||||
|
||||
#if ( configQUEUE_REGISTRY_SIZE > 0 )
|
||||
#define vQueueAddToRegistry MPU_vQueueAddToRegistry
|
||||
#define vQueueUnregisterQueue MPU_vQueueUnregisterQueue
|
||||
#define pcQueueGetName MPU_pcQueueGetName
|
||||
#endif /* #if ( configQUEUE_REGISTRY_SIZE > 0 ) */
|
||||
|
||||
/* Privileged only wrappers for Queue APIs. These are needed so that
|
||||
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||
* with all the APIs. */
|
||||
#define vQueueDelete MPU_vQueueDelete
|
||||
#define xQueueCreateMutex MPU_xQueueCreateMutex
|
||||
#define xQueueCreateMutexStatic MPU_xQueueCreateMutexStatic
|
||||
#define xQueueCreateCountingSemaphore MPU_xQueueCreateCountingSemaphore
|
||||
#define xQueueCreateCountingSemaphoreStatic MPU_xQueueCreateCountingSemaphoreStatic
|
||||
#define xQueueGenericCreate MPU_xQueueGenericCreate
|
||||
#define xQueueGenericCreateStatic MPU_xQueueGenericCreateStatic
|
||||
#define xQueueGenericReset MPU_xQueueGenericReset
|
||||
#define xQueueCreateSet MPU_xQueueCreateSet
|
||||
#define xQueueCreateSetStatic MPU_xQueueCreateSetStatic
|
||||
#define xQueueRemoveFromSet MPU_xQueueRemoveFromSet
|
||||
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||
#define xQueueGenericGetStaticBuffers MPU_xQueueGenericGetStaticBuffers
|
||||
#define xQueueGenericSendFromISR MPU_xQueueGenericSendFromISR
|
||||
#define xQueueGiveFromISR MPU_xQueueGiveFromISR
|
||||
#define xQueuePeekFromISR MPU_xQueuePeekFromISR
|
||||
#define xQueueReceiveFromISR MPU_xQueueReceiveFromISR
|
||||
#define xQueueIsQueueEmptyFromISR MPU_xQueueIsQueueEmptyFromISR
|
||||
#define xQueueIsQueueFullFromISR MPU_xQueueIsQueueFullFromISR
|
||||
#define uxQueueMessagesWaitingFromISR MPU_uxQueueMessagesWaitingFromISR
|
||||
#define xQueueGetMutexHolderFromISR MPU_xQueueGetMutexHolderFromISR
|
||||
#define xQueueSelectFromSetFromISR MPU_xQueueSelectFromSetFromISR
|
||||
#endif /* if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||
|
||||
/* Map standard timer.h API functions to the MPU equivalents. */
|
||||
#define pvTimerGetTimerID MPU_pvTimerGetTimerID
|
||||
#define vTimerSetTimerID MPU_vTimerSetTimerID
|
||||
#define xTimerIsTimerActive MPU_xTimerIsTimerActive
|
||||
#define xTimerGetTimerDaemonTaskHandle MPU_xTimerGetTimerDaemonTaskHandle
|
||||
#define xTimerGenericCommandFromTask MPU_xTimerGenericCommandFromTask
|
||||
#define pcTimerGetName MPU_pcTimerGetName
|
||||
#define vTimerSetReloadMode MPU_vTimerSetReloadMode
|
||||
#define uxTimerGetReloadMode MPU_uxTimerGetReloadMode
|
||||
#define xTimerGetPeriod MPU_xTimerGetPeriod
|
||||
#define xTimerGetExpiryTime MPU_xTimerGetExpiryTime
|
||||
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||
#define xTimerGetReloadMode MPU_xTimerGetReloadMode
|
||||
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||
|
||||
/* Privileged only wrappers for Timer APIs. These are needed so that
|
||||
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||
* with all the APIs. */
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||
#define xTimerCreate MPU_xTimerCreate
|
||||
#define xTimerCreateStatic MPU_xTimerCreateStatic
|
||||
#define xTimerGetStaticBuffer MPU_xTimerGetStaticBuffer
|
||||
#define xTimerGenericCommandFromISR MPU_xTimerGenericCommandFromISR
|
||||
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||
|
||||
/* Map standard event_group.h API functions to the MPU equivalents. */
|
||||
#define xEventGroupWaitBits MPU_xEventGroupWaitBits
|
||||
#define xEventGroupClearBits MPU_xEventGroupClearBits
|
||||
#define xEventGroupSetBits MPU_xEventGroupSetBits
|
||||
#define xEventGroupSync MPU_xEventGroupSync
|
||||
|
||||
#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_MPU_WRAPPERS_V1 == 0 ) )
|
||||
#define uxEventGroupGetNumber MPU_uxEventGroupGetNumber
|
||||
#define vEventGroupSetNumber MPU_vEventGroupSetNumber
|
||||
#endif /* #if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_MPU_WRAPPERS_V1 == 0 ) ) */
|
||||
|
||||
/* Privileged only wrappers for Event Group APIs. These are needed so that
|
||||
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||
* with all the APIs. */
|
||||
#define xEventGroupCreate MPU_xEventGroupCreate
|
||||
#define xEventGroupCreateStatic MPU_xEventGroupCreateStatic
|
||||
#define vEventGroupDelete MPU_vEventGroupDelete
|
||||
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||
#define xEventGroupGetStaticBuffer MPU_xEventGroupGetStaticBuffer
|
||||
#define xEventGroupClearBitsFromISR MPU_xEventGroupClearBitsFromISR
|
||||
#define xEventGroupSetBitsFromISR MPU_xEventGroupSetBitsFromISR
|
||||
#define xEventGroupGetBitsFromISR MPU_xEventGroupGetBitsFromISR
|
||||
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||
|
||||
/* Map standard message/stream_buffer.h API functions to the MPU
|
||||
* equivalents. */
|
||||
#define xStreamBufferSend MPU_xStreamBufferSend
|
||||
#define xStreamBufferReceive MPU_xStreamBufferReceive
|
||||
#define xStreamBufferIsFull MPU_xStreamBufferIsFull
|
||||
#define xStreamBufferIsEmpty MPU_xStreamBufferIsEmpty
|
||||
#define xStreamBufferSpacesAvailable MPU_xStreamBufferSpacesAvailable
|
||||
#define xStreamBufferBytesAvailable MPU_xStreamBufferBytesAvailable
|
||||
#define xStreamBufferSetTriggerLevel MPU_xStreamBufferSetTriggerLevel
|
||||
#define xStreamBufferNextMessageLengthBytes MPU_xStreamBufferNextMessageLengthBytes
|
||||
|
||||
/* Privileged only wrappers for Stream Buffer APIs. These are needed so that
|
||||
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||
* with all the APIs. */
|
||||
|
||||
#define xStreamBufferGenericCreate MPU_xStreamBufferGenericCreate
|
||||
#define xStreamBufferGenericCreateStatic MPU_xStreamBufferGenericCreateStatic
|
||||
#define vStreamBufferDelete MPU_vStreamBufferDelete
|
||||
#define xStreamBufferReset MPU_xStreamBufferReset
|
||||
|
||||
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||
#define xStreamBufferGetStaticBuffers MPU_xStreamBufferGetStaticBuffers
|
||||
#define xStreamBufferSendFromISR MPU_xStreamBufferSendFromISR
|
||||
#define xStreamBufferReceiveFromISR MPU_xStreamBufferReceiveFromISR
|
||||
#define xStreamBufferSendCompletedFromISR MPU_xStreamBufferSendCompletedFromISR
|
||||
#define xStreamBufferReceiveCompletedFromISR MPU_xStreamBufferReceiveCompletedFromISR
|
||||
#define xStreamBufferResetFromISR MPU_xStreamBufferResetFromISR
|
||||
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||
|
||||
#if ( ( configUSE_MPU_WRAPPERS_V1 == 0 ) && ( configENABLE_ACCESS_CONTROL_LIST == 1 ) )
|
||||
|
||||
#define vGrantAccessToTask( xTask, xTaskToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xTaskToGrantAccess ) )
|
||||
#define vRevokeAccessToTask( xTask, xTaskToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xTaskToRevokeAccess ) )
|
||||
|
||||
#define vGrantAccessToSemaphore( xTask, xSemaphoreToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xSemaphoreToGrantAccess ) )
|
||||
#define vRevokeAccessToSemaphore( xTask, xSemaphoreToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xSemaphoreToRevokeAccess ) )
|
||||
|
||||
#define vGrantAccessToQueue( xTask, xQueueToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xQueueToGrantAccess ) )
|
||||
#define vRevokeAccessToQueue( xTask, xQueueToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xQueueToRevokeAccess ) )
|
||||
|
||||
#define vGrantAccessToQueueSet( xTask, xQueueSetToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xQueueSetToGrantAccess ) )
|
||||
#define vRevokeAccessToQueueSet( xTask, xQueueSetToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xQueueSetToRevokeAccess ) )
|
||||
|
||||
#define vGrantAccessToEventGroup( xTask, xEventGroupToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xEventGroupToGrantAccess ) )
|
||||
#define vRevokeAccessToEventGroup( xTask, xEventGroupToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xEventGroupToRevokeAccess ) )
|
||||
|
||||
#define vGrantAccessToStreamBuffer( xTask, xStreamBufferToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xStreamBufferToGrantAccess ) )
|
||||
#define vRevokeAccessToStreamBuffer( xTask, xStreamBufferToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xStreamBufferToRevokeAccess ) )
|
||||
|
||||
#define vGrantAccessToMessageBuffer( xTask, xMessageBufferToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xMessageBufferToGrantAccess ) )
|
||||
#define vRevokeAccessToMessageBuffer( xTask, xMessageBufferToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xMessageBufferToRevokeAccess ) )
|
||||
|
||||
#define vGrantAccessToTimer( xTask, xTimerToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xTimerToGrantAccess ) )
|
||||
#define vRevokeAccessToTimer( xTask, xTimerToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xTimerToRevokeAccess ) )
|
||||
|
||||
#endif /* #if ( ( configUSE_MPU_WRAPPERS_V1 == 0 ) && ( configENABLE_ACCESS_CONTROL_LIST == 1 ) ) */
|
||||
|
||||
#endif /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */
|
||||
|
||||
#define PRIVILEGED_FUNCTION __attribute__( ( section( "privileged_functions" ) ) )
|
||||
#define PRIVILEGED_DATA __attribute__( ( section( "privileged_data" ) ) )
|
||||
#define FREERTOS_SYSTEM_CALL __attribute__( ( section( "freertos_system_calls" ) ) )
|
||||
|
||||
#else /* portUSING_MPU_WRAPPERS */
|
||||
|
||||
#define PRIVILEGED_FUNCTION
|
||||
#define PRIVILEGED_DATA
|
||||
#define FREERTOS_SYSTEM_CALL
|
||||
|
||||
#endif /* portUSING_MPU_WRAPPERS */
|
||||
|
||||
|
||||
#endif /* MPU_WRAPPERS_H */
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INC_NEWLIB_FREERTOS_H
|
||||
#define INC_NEWLIB_FREERTOS_H
|
||||
|
||||
/* Note Newlib support has been included by popular demand, but is not
|
||||
* used by the FreeRTOS maintainers themselves. FreeRTOS is not
|
||||
* responsible for resulting newlib operation. User must be familiar with
|
||||
* newlib and must provide system-wide implementations of the necessary
|
||||
* stubs. Be warned that (at the time of writing) the current newlib design
|
||||
* implements a system-wide malloc() that must be provided with locks.
|
||||
*
|
||||
* See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
|
||||
* for additional information. */
|
||||
|
||||
#include <reent.h>
|
||||
|
||||
#define configUSE_C_RUNTIME_TLS_SUPPORT 1
|
||||
|
||||
#ifndef configTLS_BLOCK_TYPE
|
||||
#define configTLS_BLOCK_TYPE struct _reent
|
||||
#endif
|
||||
|
||||
#ifndef configINIT_TLS_BLOCK
|
||||
#define configINIT_TLS_BLOCK( xTLSBlock, pxTopOfStack ) _REENT_INIT_PTR( &( xTLSBlock ) )
|
||||
#endif
|
||||
|
||||
#ifndef configSET_TLS_BLOCK
|
||||
#define configSET_TLS_BLOCK( xTLSBlock ) ( _impure_ptr = &( xTLSBlock ) )
|
||||
#endif
|
||||
|
||||
#ifndef configDEINIT_TLS_BLOCK
|
||||
#define configDEINIT_TLS_BLOCK( xTLSBlock ) _reclaim_reent( &( xTLSBlock ) )
|
||||
#endif
|
||||
|
||||
#endif /* INC_NEWLIB_FREERTOS_H */
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INC_PICOLIBC_FREERTOS_H
|
||||
#define INC_PICOLIBC_FREERTOS_H
|
||||
|
||||
/* Use picolibc TLS support to allocate space for __thread variables,
|
||||
* initialize them at thread creation and set the TLS context at
|
||||
* thread switch time.
|
||||
*
|
||||
* See the picolibc TLS docs:
|
||||
* https://github.com/picolibc/picolibc/blob/main/doc/tls.md
|
||||
* for additional information. */
|
||||
|
||||
#include <picotls.h>
|
||||
|
||||
#define configUSE_C_RUNTIME_TLS_SUPPORT 1
|
||||
|
||||
#define configTLS_BLOCK_TYPE void *
|
||||
|
||||
#define picolibcTLS_SIZE ( ( portPOINTER_SIZE_TYPE ) _tls_size() )
|
||||
#define picolibcSTACK_ALIGNMENT_MASK ( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK )
|
||||
|
||||
#if __PICOLIBC_MAJOR__ > 1 || __PICOLIBC_MINOR__ >= 8
|
||||
|
||||
/* Picolibc 1.8 and newer have explicit alignment values provided
|
||||
* by the _tls_align() inline */
|
||||
#define picolibcTLS_ALIGNMENT_MASK ( ( portPOINTER_SIZE_TYPE ) ( _tls_align() - 1 ) )
|
||||
#else
|
||||
|
||||
/* For older Picolibc versions, use the general port alignment value */
|
||||
#define picolibcTLS_ALIGNMENT_MASK ( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK )
|
||||
#endif
|
||||
|
||||
/* Allocate thread local storage block off the end of the
|
||||
* stack. The picolibcTLS_SIZE macro returns the size (in
|
||||
* bytes) of the total TLS area used by the application.
|
||||
* Calculate the top of stack address. */
|
||||
#if ( portSTACK_GROWTH < 0 )
|
||||
|
||||
#define configINIT_TLS_BLOCK( xTLSBlock, pxTopOfStack ) \
|
||||
do { \
|
||||
xTLSBlock = ( void * ) ( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) - \
|
||||
picolibcTLS_SIZE ) & \
|
||||
~picolibcTLS_ALIGNMENT_MASK ); \
|
||||
pxTopOfStack = ( StackType_t * ) ( ( ( ( portPOINTER_SIZE_TYPE ) xTLSBlock ) - 1 ) & \
|
||||
~picolibcSTACK_ALIGNMENT_MASK ); \
|
||||
_init_tls( xTLSBlock ); \
|
||||
} while( 0 )
|
||||
#else /* portSTACK_GROWTH */
|
||||
#define configINIT_TLS_BLOCK( xTLSBlock, pxTopOfStack ) \
|
||||
do { \
|
||||
xTLSBlock = ( void * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack + \
|
||||
picolibcTLS_ALIGNMENT_MASK ) & ~picolibcTLS_ALIGNMENT_MASK ); \
|
||||
pxTopOfStack = ( StackType_t * ) ( ( ( ( ( portPOINTER_SIZE_TYPE ) xTLSBlock ) + \
|
||||
picolibcTLS_SIZE ) + picolibcSTACK_ALIGNMENT_MASK ) & \
|
||||
~picolibcSTACK_ALIGNMENT_MASK ); \
|
||||
_init_tls( xTLSBlock ); \
|
||||
} while( 0 )
|
||||
#endif /* portSTACK_GROWTH */
|
||||
|
||||
#define configSET_TLS_BLOCK( xTLSBlock ) _set_tls( xTLSBlock )
|
||||
|
||||
#define configDEINIT_TLS_BLOCK( xTLSBlock )
|
||||
|
||||
#endif /* INC_PICOLIBC_FREERTOS_H */
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Portable layer API. Each function must be defined for each port.
|
||||
*----------------------------------------------------------*/
|
||||
|
||||
#ifndef PORTABLE_H
|
||||
#define PORTABLE_H
|
||||
|
||||
/* Each FreeRTOS port has a unique portmacro.h header file. Originally a
|
||||
* pre-processor definition was used to ensure the pre-processor found the correct
|
||||
* portmacro.h file for the port being used. That scheme was deprecated in favour
|
||||
* of setting the compiler's include path such that it found the correct
|
||||
* portmacro.h file - removing the need for the constant and allowing the
|
||||
* portmacro.h file to be located anywhere in relation to the port being used.
|
||||
* Purely for reasons of backward compatibility the old method is still valid, but
|
||||
* to make it clear that new projects should not use it, support for the port
|
||||
* specific constants has been moved into the deprecated_definitions.h header
|
||||
* file. */
|
||||
#include "deprecated_definitions.h"
|
||||
|
||||
/* If portENTER_CRITICAL is not defined then including deprecated_definitions.h
|
||||
* did not result in a portmacro.h header file being included - and it should be
|
||||
* included here. In this case the path to the correct portmacro.h header file
|
||||
* must be set in the compiler's include path. */
|
||||
#ifndef portENTER_CRITICAL
|
||||
#include "portmacro.h"
|
||||
#endif
|
||||
|
||||
#if portBYTE_ALIGNMENT == 32
|
||||
#define portBYTE_ALIGNMENT_MASK ( 0x001f )
|
||||
#elif portBYTE_ALIGNMENT == 16
|
||||
#define portBYTE_ALIGNMENT_MASK ( 0x000f )
|
||||
#elif portBYTE_ALIGNMENT == 8
|
||||
#define portBYTE_ALIGNMENT_MASK ( 0x0007 )
|
||||
#elif portBYTE_ALIGNMENT == 4
|
||||
#define portBYTE_ALIGNMENT_MASK ( 0x0003 )
|
||||
#elif portBYTE_ALIGNMENT == 2
|
||||
#define portBYTE_ALIGNMENT_MASK ( 0x0001 )
|
||||
#elif portBYTE_ALIGNMENT == 1
|
||||
#define portBYTE_ALIGNMENT_MASK ( 0x0000 )
|
||||
#else /* if portBYTE_ALIGNMENT == 32 */
|
||||
#error "Invalid portBYTE_ALIGNMENT definition"
|
||||
#endif /* if portBYTE_ALIGNMENT == 32 */
|
||||
|
||||
#ifndef portUSING_MPU_WRAPPERS
|
||||
#define portUSING_MPU_WRAPPERS 0
|
||||
#endif
|
||||
|
||||
#ifndef portNUM_CONFIGURABLE_REGIONS
|
||||
#define portNUM_CONFIGURABLE_REGIONS 1
|
||||
#endif
|
||||
|
||||
#ifndef portHAS_STACK_OVERFLOW_CHECKING
|
||||
#define portHAS_STACK_OVERFLOW_CHECKING 0
|
||||
#endif
|
||||
|
||||
#ifndef portARCH_NAME
|
||||
#define portARCH_NAME NULL
|
||||
#endif
|
||||
|
||||
#ifndef portBASE_TYPE_ENTER_CRITICAL
|
||||
#define portBASE_TYPE_ENTER_CRITICAL() taskENTER_CRITICAL()
|
||||
#endif
|
||||
|
||||
#ifndef portBASE_TYPE_EXIT_CRITICAL
|
||||
#define portBASE_TYPE_EXIT_CRITICAL() taskEXIT_CRITICAL()
|
||||
#endif
|
||||
|
||||
#ifndef configSTACK_DEPTH_TYPE
|
||||
#define configSTACK_DEPTH_TYPE StackType_t
|
||||
#endif
|
||||
|
||||
#ifndef configSTACK_ALLOCATION_FROM_SEPARATE_HEAP
|
||||
/* Defaults to 0 for backward compatibility. */
|
||||
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0
|
||||
#endif
|
||||
|
||||
#include "mpu_wrappers.h"
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/*
|
||||
* Setup the stack of a new task so it is ready to be placed under the
|
||||
* scheduler control. The registers have to be placed on the stack in
|
||||
* the order that the port expects to find them.
|
||||
*
|
||||
*/
|
||||
#if ( portUSING_MPU_WRAPPERS == 1 )
|
||||
#if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
|
||||
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
|
||||
StackType_t * pxEndOfStack,
|
||||
TaskFunction_t pxCode,
|
||||
void * pvParameters,
|
||||
BaseType_t xRunPrivileged,
|
||||
xMPU_SETTINGS * xMPUSettings ) PRIVILEGED_FUNCTION;
|
||||
#else
|
||||
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
|
||||
TaskFunction_t pxCode,
|
||||
void * pvParameters,
|
||||
BaseType_t xRunPrivileged,
|
||||
xMPU_SETTINGS * xMPUSettings ) PRIVILEGED_FUNCTION;
|
||||
#endif /* if ( portHAS_STACK_OVERFLOW_CHECKING == 1 ) */
|
||||
#else /* if ( portUSING_MPU_WRAPPERS == 1 ) */
|
||||
#if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
|
||||
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
|
||||
StackType_t * pxEndOfStack,
|
||||
TaskFunction_t pxCode,
|
||||
void * pvParameters ) PRIVILEGED_FUNCTION;
|
||||
#else
|
||||
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
|
||||
TaskFunction_t pxCode,
|
||||
void * pvParameters ) PRIVILEGED_FUNCTION;
|
||||
#endif
|
||||
#endif /* if ( portUSING_MPU_WRAPPERS == 1 ) */
|
||||
|
||||
/* Used by heap_5.c to define the start address and size of each memory region
|
||||
* that together comprise the total FreeRTOS heap space. */
|
||||
typedef struct HeapRegion
|
||||
{
|
||||
uint8_t * pucStartAddress;
|
||||
size_t xSizeInBytes;
|
||||
} HeapRegion_t;
|
||||
|
||||
/* Used to pass information about the heap out of vPortGetHeapStats(). */
|
||||
typedef struct xHeapStats
|
||||
{
|
||||
size_t xAvailableHeapSpaceInBytes; /* The total heap size currently available - this is the sum of all the free blocks, not the largest block that can be allocated. */
|
||||
size_t xSizeOfLargestFreeBlockInBytes; /* The maximum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */
|
||||
size_t xSizeOfSmallestFreeBlockInBytes; /* The minimum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */
|
||||
size_t xNumberOfFreeBlocks; /* The number of free memory blocks within the heap at the time vPortGetHeapStats() is called. */
|
||||
size_t xMinimumEverFreeBytesRemaining; /* The minimum amount of total free memory (sum of all free blocks) there has been in the heap since the system booted. */
|
||||
size_t xNumberOfSuccessfulAllocations; /* The number of calls to pvPortMalloc() that have returned a valid memory block. */
|
||||
size_t xNumberOfSuccessfulFrees; /* The number of calls to vPortFree() that has successfully freed a block of memory. */
|
||||
} HeapStats_t;
|
||||
|
||||
/*
|
||||
* Used to define multiple heap regions for use by heap_5.c. This function
|
||||
* must be called before any calls to pvPortMalloc() - not creating a task,
|
||||
* queue, semaphore, mutex, software timer, event group, etc. will result in
|
||||
* pvPortMalloc being called.
|
||||
*
|
||||
* pxHeapRegions passes in an array of HeapRegion_t structures - each of which
|
||||
* defines a region of memory that can be used as the heap. The array is
|
||||
* terminated by a HeapRegions_t structure that has a size of 0. The region
|
||||
* with the lowest start address must appear first in the array.
|
||||
*/
|
||||
void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/*
|
||||
* Returns a HeapStats_t structure filled with information about the current
|
||||
* heap state.
|
||||
*/
|
||||
void vPortGetHeapStats( HeapStats_t * pxHeapStats );
|
||||
|
||||
/*
|
||||
* Map to the memory management routines required for the port.
|
||||
*/
|
||||
void * pvPortMalloc( size_t xWantedSize ) PRIVILEGED_FUNCTION;
|
||||
void * pvPortCalloc( size_t xNum,
|
||||
size_t xSize ) PRIVILEGED_FUNCTION;
|
||||
void vPortFree( void * pv ) PRIVILEGED_FUNCTION;
|
||||
void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION;
|
||||
size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION;
|
||||
size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION;
|
||||
void xPortResetHeapMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION;
|
||||
|
||||
#if ( configSTACK_ALLOCATION_FROM_SEPARATE_HEAP == 1 )
|
||||
void * pvPortMallocStack( size_t xSize ) PRIVILEGED_FUNCTION;
|
||||
void vPortFreeStack( void * pv ) PRIVILEGED_FUNCTION;
|
||||
#else
|
||||
#define pvPortMallocStack pvPortMalloc
|
||||
#define vPortFreeStack vPortFree
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This function resets the internal state of the heap module. It must be called
|
||||
* by the application before restarting the scheduler.
|
||||
*/
|
||||
void vPortHeapResetState( void ) PRIVILEGED_FUNCTION;
|
||||
|
||||
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
|
||||
|
||||
/**
|
||||
* task.h
|
||||
* @code{c}
|
||||
* void vApplicationMallocFailedHook( void )
|
||||
* @endcode
|
||||
*
|
||||
* This hook function is called when allocation failed.
|
||||
*/
|
||||
void vApplicationMallocFailedHook( void );
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Setup the hardware ready for the scheduler to take control. This generally
|
||||
* sets up a tick interrupt and sets timers for the correct tick frequency.
|
||||
*/
|
||||
BaseType_t xPortStartScheduler( void ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/*
|
||||
* Undo any hardware/ISR setup that was performed by xPortStartScheduler() so
|
||||
* the hardware is left in its original condition after the scheduler stops
|
||||
* executing.
|
||||
*/
|
||||
void vPortEndScheduler( void ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/*
|
||||
* The structures and methods of manipulating the MPU are contained within the
|
||||
* port layer.
|
||||
*
|
||||
* Fills the xMPUSettings structure with the memory region information
|
||||
* contained in xRegions.
|
||||
*/
|
||||
#if ( portUSING_MPU_WRAPPERS == 1 )
|
||||
struct xMEMORY_REGION;
|
||||
void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings,
|
||||
const struct xMEMORY_REGION * const xRegions,
|
||||
StackType_t * pxBottomOfStack,
|
||||
configSTACK_DEPTH_TYPE uxStackDepth ) PRIVILEGED_FUNCTION;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Checks if the calling task is authorized to access the given buffer.
|
||||
*
|
||||
* @param pvBuffer The buffer which the calling task wants to access.
|
||||
* @param ulBufferLength The length of the pvBuffer.
|
||||
* @param ulAccessRequested The permissions that the calling task wants.
|
||||
*
|
||||
* @return pdTRUE if the calling task is authorized to access the buffer,
|
||||
* pdFALSE otherwise.
|
||||
*/
|
||||
#if ( portUSING_MPU_WRAPPERS == 1 )
|
||||
BaseType_t xPortIsAuthorizedToAccessBuffer( const void * pvBuffer,
|
||||
uint32_t ulBufferLength,
|
||||
uint32_t ulAccessRequested ) PRIVILEGED_FUNCTION;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Checks if the calling task is authorized to access the given kernel object.
|
||||
*
|
||||
* @param lInternalIndexOfKernelObject The index of the kernel object in the kernel
|
||||
* object handle pool.
|
||||
*
|
||||
* @return pdTRUE if the calling task is authorized to access the kernel object,
|
||||
* pdFALSE otherwise.
|
||||
*/
|
||||
#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configUSE_MPU_WRAPPERS_V1 == 0 ) )
|
||||
|
||||
BaseType_t xPortIsAuthorizedToAccessKernelObject( int32_t lInternalIndexOfKernelObject ) PRIVILEGED_FUNCTION;
|
||||
|
||||
#endif
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
#endif /* PORTABLE_H */
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PROJDEFS_H
|
||||
#define PROJDEFS_H
|
||||
|
||||
/*
|
||||
* Defines the prototype to which task functions must conform. Defined in this
|
||||
* file to ensure the type is known before portable.h is included.
|
||||
*/
|
||||
typedef void (* TaskFunction_t)( void * arg );
|
||||
|
||||
/* Converts a time in milliseconds to a time in ticks. This macro can be
|
||||
* overridden by a macro of the same name defined in FreeRTOSConfig.h in case the
|
||||
* definition here is not suitable for your application. */
|
||||
#ifndef pdMS_TO_TICKS
|
||||
#define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( ( ( uint64_t ) ( xTimeInMs ) * ( uint64_t ) configTICK_RATE_HZ ) / ( uint64_t ) 1000U ) )
|
||||
#endif
|
||||
|
||||
/* Converts a time in ticks to a time in milliseconds. This macro can be
|
||||
* overridden by a macro of the same name defined in FreeRTOSConfig.h in case the
|
||||
* definition here is not suitable for your application. */
|
||||
#ifndef pdTICKS_TO_MS
|
||||
#define pdTICKS_TO_MS( xTimeInTicks ) ( ( TickType_t ) ( ( ( uint64_t ) ( xTimeInTicks ) * ( uint64_t ) 1000U ) / ( uint64_t ) configTICK_RATE_HZ ) )
|
||||
#endif
|
||||
|
||||
#define pdFALSE ( ( BaseType_t ) 0 )
|
||||
#define pdTRUE ( ( BaseType_t ) 1 )
|
||||
#define pdFALSE_SIGNED ( ( BaseType_t ) 0 )
|
||||
#define pdTRUE_SIGNED ( ( BaseType_t ) 1 )
|
||||
#define pdFALSE_UNSIGNED ( ( UBaseType_t ) 0 )
|
||||
#define pdTRUE_UNSIGNED ( ( UBaseType_t ) 1 )
|
||||
|
||||
#define pdPASS ( pdTRUE )
|
||||
#define pdFAIL ( pdFALSE )
|
||||
#define errQUEUE_EMPTY ( ( BaseType_t ) 0 )
|
||||
#define errQUEUE_FULL ( ( BaseType_t ) 0 )
|
||||
|
||||
/* FreeRTOS error definitions. */
|
||||
#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 )
|
||||
#define errQUEUE_BLOCKED ( -4 )
|
||||
#define errQUEUE_YIELD ( -5 )
|
||||
|
||||
/* Macros used for basic data corruption checks. */
|
||||
#ifndef configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES
|
||||
#define configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 0
|
||||
#endif
|
||||
|
||||
#if ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS )
|
||||
#define pdINTEGRITY_CHECK_VALUE 0x5a5a
|
||||
#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_32_BITS )
|
||||
#define pdINTEGRITY_CHECK_VALUE 0x5a5a5a5aUL
|
||||
#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_64_BITS )
|
||||
#define pdINTEGRITY_CHECK_VALUE 0x5a5a5a5a5a5a5a5aULL
|
||||
#else
|
||||
#error configTICK_TYPE_WIDTH_IN_BITS set to unsupported tick type width.
|
||||
#endif
|
||||
|
||||
/* The following errno values are used by FreeRTOS+ components, not FreeRTOS
|
||||
* itself. */
|
||||
#define pdFREERTOS_ERRNO_NONE 0 /* No errors */
|
||||
#define pdFREERTOS_ERRNO_ENOENT 2 /* No such file or directory */
|
||||
#define pdFREERTOS_ERRNO_EINTR 4 /* Interrupted system call */
|
||||
#define pdFREERTOS_ERRNO_EIO 5 /* I/O error */
|
||||
#define pdFREERTOS_ERRNO_ENXIO 6 /* No such device or address */
|
||||
#define pdFREERTOS_ERRNO_EBADF 9 /* Bad file number */
|
||||
#define pdFREERTOS_ERRNO_EAGAIN 11 /* No more processes */
|
||||
#define pdFREERTOS_ERRNO_EWOULDBLOCK 11 /* Operation would block */
|
||||
#define pdFREERTOS_ERRNO_ENOMEM 12 /* Not enough memory */
|
||||
#define pdFREERTOS_ERRNO_EACCES 13 /* Permission denied */
|
||||
#define pdFREERTOS_ERRNO_EFAULT 14 /* Bad address */
|
||||
#define pdFREERTOS_ERRNO_EBUSY 16 /* Mount device busy */
|
||||
#define pdFREERTOS_ERRNO_EEXIST 17 /* File exists */
|
||||
#define pdFREERTOS_ERRNO_EXDEV 18 /* Cross-device link */
|
||||
#define pdFREERTOS_ERRNO_ENODEV 19 /* No such device */
|
||||
#define pdFREERTOS_ERRNO_ENOTDIR 20 /* Not a directory */
|
||||
#define pdFREERTOS_ERRNO_EISDIR 21 /* Is a directory */
|
||||
#define pdFREERTOS_ERRNO_EINVAL 22 /* Invalid argument */
|
||||
#define pdFREERTOS_ERRNO_ENOSPC 28 /* No space left on device */
|
||||
#define pdFREERTOS_ERRNO_ESPIPE 29 /* Illegal seek */
|
||||
#define pdFREERTOS_ERRNO_EROFS 30 /* Read only file system */
|
||||
#define pdFREERTOS_ERRNO_EUNATCH 42 /* Protocol driver not attached */
|
||||
#define pdFREERTOS_ERRNO_EBADE 50 /* Invalid exchange */
|
||||
#define pdFREERTOS_ERRNO_EFTYPE 79 /* Inappropriate file type or format */
|
||||
#define pdFREERTOS_ERRNO_ENMFILE 89 /* No more files */
|
||||
#define pdFREERTOS_ERRNO_ENOTEMPTY 90 /* Directory not empty */
|
||||
#define pdFREERTOS_ERRNO_ENAMETOOLONG 91 /* File or path name too long */
|
||||
#define pdFREERTOS_ERRNO_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
|
||||
#define pdFREERTOS_ERRNO_EAFNOSUPPORT 97 /* Address family not supported by protocol */
|
||||
#define pdFREERTOS_ERRNO_ENOBUFS 105 /* No buffer space available */
|
||||
#define pdFREERTOS_ERRNO_ENOPROTOOPT 109 /* Protocol not available */
|
||||
#define pdFREERTOS_ERRNO_EADDRINUSE 112 /* Address already in use */
|
||||
#define pdFREERTOS_ERRNO_ETIMEDOUT 116 /* Connection timed out */
|
||||
#define pdFREERTOS_ERRNO_EINPROGRESS 119 /* Connection already in progress */
|
||||
#define pdFREERTOS_ERRNO_EALREADY 120 /* Socket already connected */
|
||||
#define pdFREERTOS_ERRNO_EADDRNOTAVAIL 125 /* Address not available */
|
||||
#define pdFREERTOS_ERRNO_EISCONN 127 /* Socket is already connected */
|
||||
#define pdFREERTOS_ERRNO_ENOTCONN 128 /* Socket is not connected */
|
||||
#define pdFREERTOS_ERRNO_ENOMEDIUM 135 /* No medium inserted */
|
||||
#define pdFREERTOS_ERRNO_EILSEQ 138 /* An invalid UTF-16 sequence was encountered. */
|
||||
#define pdFREERTOS_ERRNO_ECANCELED 140 /* Operation canceled. */
|
||||
|
||||
/* The following endian values are used by FreeRTOS+ components, not FreeRTOS
|
||||
* itself. */
|
||||
#define pdFREERTOS_LITTLE_ENDIAN 0
|
||||
#define pdFREERTOS_BIG_ENDIAN 1
|
||||
|
||||
/* Re-defining endian values for generic naming. */
|
||||
#define pdLITTLE_ENDIAN pdFREERTOS_LITTLE_ENDIAN
|
||||
#define pdBIG_ENDIAN pdFREERTOS_BIG_ENDIAN
|
||||
|
||||
|
||||
#endif /* PROJDEFS_H */
|
||||
+1882
File diff suppressed because it is too large
Load Diff
+1215
File diff suppressed because it is too large
Load Diff
+155
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STACK_MACROS_H
|
||||
#define STACK_MACROS_H
|
||||
|
||||
/*
|
||||
* Call the stack overflow hook function if the stack of the task being swapped
|
||||
* out is currently overflowed, or looks like it might have overflowed in the
|
||||
* past.
|
||||
*
|
||||
* Setting configCHECK_FOR_STACK_OVERFLOW to 1 will cause the macro to check
|
||||
* the current stack state only - comparing the current top of stack value to
|
||||
* the stack limit. Setting configCHECK_FOR_STACK_OVERFLOW to greater than 1
|
||||
* will also cause the last few stack bytes to be checked to ensure the value
|
||||
* to which the bytes were set when the task was created have not been
|
||||
* overwritten. Note this second test does not guarantee that an overflowed
|
||||
* stack will always be recognised.
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* portSTACK_LIMIT_PADDING is a number of extra words to consider to be in
|
||||
* use on the stack.
|
||||
*/
|
||||
#ifndef portSTACK_LIMIT_PADDING
|
||||
#define portSTACK_LIMIT_PADDING 0
|
||||
#endif
|
||||
|
||||
/* Stack overflow check is not straight forward to implement for MPU ports
|
||||
* because of the following reasons:
|
||||
* 1. The context is stored in TCB and as a result, pxTopOfStack member points
|
||||
* to the context location in TCB.
|
||||
* 2. System calls are executed on a separate privileged only stack.
|
||||
*
|
||||
* It is still okay because an MPU region is used to protect task stack which
|
||||
* means task stack overflow will trigger an MPU fault for unprivileged tasks.
|
||||
* Additionally, architectures with hardware stack overflow checking support
|
||||
* (such as Armv8-M) will trigger a fault when a task's stack overflows.
|
||||
*/
|
||||
#if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) && ( portUSING_MPU_WRAPPERS != 1 ) )
|
||||
|
||||
/* Only the current stack state is to be checked. */
|
||||
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||
do \
|
||||
{ \
|
||||
/* Is the currently saved stack pointer within the stack limit? */ \
|
||||
if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack + portSTACK_LIMIT_PADDING ) \
|
||||
{ \
|
||||
char * pcOverflowTaskName = pxCurrentTCB->pcTaskName; \
|
||||
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pcOverflowTaskName ); \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH > 0 ) && ( portUSING_MPU_WRAPPERS != 1 ) )
|
||||
|
||||
/* Only the current stack state is to be checked. */
|
||||
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||
do \
|
||||
{ \
|
||||
/* Is the currently saved stack pointer within the stack limit? */ \
|
||||
if( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack - portSTACK_LIMIT_PADDING ) \
|
||||
{ \
|
||||
char * pcOverflowTaskName = pxCurrentTCB->pcTaskName; \
|
||||
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pcOverflowTaskName ); \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) && ( portUSING_MPU_WRAPPERS != 1 ) )
|
||||
|
||||
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||
do \
|
||||
{ \
|
||||
const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack; \
|
||||
const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5U; \
|
||||
\
|
||||
if( ( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack + portSTACK_LIMIT_PADDING ) || \
|
||||
( pulStack[ 0 ] != ulCheckValue ) || \
|
||||
( pulStack[ 1 ] != ulCheckValue ) || \
|
||||
( pulStack[ 2 ] != ulCheckValue ) || \
|
||||
( pulStack[ 3 ] != ulCheckValue ) ) \
|
||||
{ \
|
||||
char * pcOverflowTaskName = pxCurrentTCB->pcTaskName; \
|
||||
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pcOverflowTaskName ); \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH > 0 ) && ( portUSING_MPU_WRAPPERS != 1 ) )
|
||||
|
||||
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||
do \
|
||||
{ \
|
||||
int8_t * pcEndOfStack = ( int8_t * ) pxCurrentTCB->pxEndOfStack; \
|
||||
static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \
|
||||
tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \
|
||||
tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \
|
||||
tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \
|
||||
tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \
|
||||
\
|
||||
pcEndOfStack -= sizeof( ucExpectedStackBytes ); \
|
||||
\
|
||||
if( ( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack - portSTACK_LIMIT_PADDING ) || \
|
||||
( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) ) \
|
||||
{ \
|
||||
char * pcOverflowTaskName = pxCurrentTCB->pcTaskName; \
|
||||
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pcOverflowTaskName ); \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Remove stack overflow macro if not being used. */
|
||||
#ifndef taskCHECK_FOR_STACK_OVERFLOW
|
||||
#define taskCHECK_FOR_STACK_OVERFLOW()
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* STACK_MACROS_H */
|
||||
+1280
File diff suppressed because it is too large
Load Diff
+3800
File diff suppressed because it is too large
Load Diff
+1434
File diff suppressed because it is too large
Load Diff
Vendored
+248
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
|
||||
* all the API functions to use the MPU wrappers. That should only be done when
|
||||
* task.h is included from an application file. */
|
||||
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "list.h"
|
||||
|
||||
/* The MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be
|
||||
* defined for the header files above, but not in this file, in order to
|
||||
* generate the correct privileged Vs unprivileged linkage and placement. */
|
||||
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* PUBLIC LIST API documented in list.h
|
||||
*----------------------------------------------------------*/
|
||||
|
||||
void vListInitialise( List_t * const pxList )
|
||||
{
|
||||
traceENTER_vListInitialise( pxList );
|
||||
|
||||
/* The list structure contains a list item which is used to mark the
|
||||
* end of the list. To initialise the list the list end is inserted
|
||||
* as the only list entry. */
|
||||
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
|
||||
|
||||
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );
|
||||
|
||||
/* The list end value is the highest possible value in the list to
|
||||
* ensure it remains at the end of the list. */
|
||||
pxList->xListEnd.xItemValue = portMAX_DELAY;
|
||||
|
||||
/* The list end next and previous pointers point to itself so we know
|
||||
* when the list is empty. */
|
||||
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
|
||||
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
|
||||
|
||||
/* Initialize the remaining fields of xListEnd when it is a proper ListItem_t */
|
||||
#if ( configUSE_MINI_LIST_ITEM == 0 )
|
||||
{
|
||||
pxList->xListEnd.pvOwner = NULL;
|
||||
pxList->xListEnd.pxContainer = NULL;
|
||||
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
|
||||
|
||||
/* Write known values into the list if
|
||||
* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
|
||||
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
|
||||
|
||||
traceRETURN_vListInitialise();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vListInitialiseItem( ListItem_t * const pxItem )
|
||||
{
|
||||
traceENTER_vListInitialiseItem( pxItem );
|
||||
|
||||
/* Make sure the list item is not recorded as being on a list. */
|
||||
pxItem->pxContainer = NULL;
|
||||
|
||||
/* Write known values into the list item if
|
||||
* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
|
||||
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
|
||||
|
||||
traceRETURN_vListInitialiseItem();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vListInsertEnd( List_t * const pxList,
|
||||
ListItem_t * const pxNewListItem )
|
||||
{
|
||||
ListItem_t * const pxIndex = pxList->pxIndex;
|
||||
|
||||
traceENTER_vListInsertEnd( pxList, pxNewListItem );
|
||||
|
||||
/* Only effective when configASSERT() is also defined, these tests may catch
|
||||
* the list data structures being overwritten in memory. They will not catch
|
||||
* data errors caused by incorrect configuration or use of FreeRTOS. */
|
||||
listTEST_LIST_INTEGRITY( pxList );
|
||||
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
|
||||
|
||||
/* Insert a new list item into pxList, but rather than sort the list,
|
||||
* makes the new list item the last item to be removed by a call to
|
||||
* listGET_OWNER_OF_NEXT_ENTRY(). */
|
||||
pxNewListItem->pxNext = pxIndex;
|
||||
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
|
||||
|
||||
/* Only used during decision coverage testing. */
|
||||
mtCOVERAGE_TEST_DELAY();
|
||||
|
||||
pxIndex->pxPrevious->pxNext = pxNewListItem;
|
||||
pxIndex->pxPrevious = pxNewListItem;
|
||||
|
||||
/* Remember which list the item is in. */
|
||||
pxNewListItem->pxContainer = pxList;
|
||||
|
||||
( pxList->uxNumberOfItems ) = ( UBaseType_t ) ( pxList->uxNumberOfItems + 1U );
|
||||
|
||||
traceRETURN_vListInsertEnd();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vListInsert( List_t * const pxList,
|
||||
ListItem_t * const pxNewListItem )
|
||||
{
|
||||
ListItem_t * pxIterator;
|
||||
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
|
||||
|
||||
traceENTER_vListInsert( pxList, pxNewListItem );
|
||||
|
||||
/* Only effective when configASSERT() is also defined, these tests may catch
|
||||
* the list data structures being overwritten in memory. They will not catch
|
||||
* data errors caused by incorrect configuration or use of FreeRTOS. */
|
||||
listTEST_LIST_INTEGRITY( pxList );
|
||||
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
|
||||
|
||||
/* Insert the new list item into the list, sorted in xItemValue order.
|
||||
*
|
||||
* If the list already contains a list item with the same item value then the
|
||||
* new list item should be placed after it. This ensures that TCBs which are
|
||||
* stored in ready lists (all of which have the same xItemValue value) get a
|
||||
* share of the CPU. However, if the xItemValue is the same as the back marker
|
||||
* the iteration loop below will not end. Therefore the value is checked
|
||||
* first, and the algorithm slightly modified if necessary. */
|
||||
if( xValueOfInsertion == portMAX_DELAY )
|
||||
{
|
||||
pxIterator = pxList->xListEnd.pxPrevious;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* *** NOTE ***********************************************************
|
||||
* If you find your application is crashing here then likely causes are
|
||||
* listed below. In addition see https://www.freertos.org/Why-FreeRTOS/FAQs for
|
||||
* more tips, and ensure configASSERT() is defined!
|
||||
* https://www.FreeRTOS.org/a00110.html#configASSERT
|
||||
*
|
||||
* 1) Stack overflow -
|
||||
* see https://www.FreeRTOS.org/Stacks-and-stack-overflow-checking.html
|
||||
* 2) Incorrect interrupt priority assignment, especially on Cortex-M
|
||||
* parts where numerically high priority values denote low actual
|
||||
* interrupt priorities, which can seem counter intuitive. See
|
||||
* https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html and the definition
|
||||
* of configMAX_SYSCALL_INTERRUPT_PRIORITY on
|
||||
* https://www.FreeRTOS.org/a00110.html
|
||||
* 3) Calling an API function from within a critical section or when
|
||||
* the scheduler is suspended, or calling an API function that does
|
||||
* not end in "FromISR" from an interrupt.
|
||||
* 4) Using a queue or semaphore before it has been initialised or
|
||||
* before the scheduler has been started (are interrupts firing
|
||||
* before vTaskStartScheduler() has been called?).
|
||||
* 5) If the FreeRTOS port supports interrupt nesting then ensure that
|
||||
* the priority of the tick interrupt is at or below
|
||||
* configMAX_SYSCALL_INTERRUPT_PRIORITY.
|
||||
**********************************************************************/
|
||||
|
||||
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )
|
||||
{
|
||||
/* There is nothing to do here, just iterating to the wanted
|
||||
* insertion position.
|
||||
* IF YOU FIND YOUR CODE STUCK HERE, SEE THE NOTE JUST ABOVE.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
pxNewListItem->pxNext = pxIterator->pxNext;
|
||||
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
|
||||
pxNewListItem->pxPrevious = pxIterator;
|
||||
pxIterator->pxNext = pxNewListItem;
|
||||
|
||||
/* Remember which list the item is in. This allows fast removal of the
|
||||
* item later. */
|
||||
pxNewListItem->pxContainer = pxList;
|
||||
|
||||
( pxList->uxNumberOfItems ) = ( UBaseType_t ) ( pxList->uxNumberOfItems + 1U );
|
||||
|
||||
traceRETURN_vListInsert();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
|
||||
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
|
||||
{
|
||||
/* The list item knows which list it is in. Obtain the list from the list
|
||||
* item. */
|
||||
List_t * const pxList = pxItemToRemove->pxContainer;
|
||||
|
||||
traceENTER_uxListRemove( pxItemToRemove );
|
||||
|
||||
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
|
||||
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
|
||||
|
||||
/* Only used during decision coverage testing. */
|
||||
mtCOVERAGE_TEST_DELAY();
|
||||
|
||||
/* Make sure the index is left pointing to a valid item. */
|
||||
if( pxList->pxIndex == pxItemToRemove )
|
||||
{
|
||||
pxList->pxIndex = pxItemToRemove->pxPrevious;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
pxItemToRemove->pxContainer = NULL;
|
||||
( pxList->uxNumberOfItems ) = ( UBaseType_t ) ( pxList->uxNumberOfItems - 1U );
|
||||
|
||||
traceRETURN_uxListRemove( pxList->uxNumberOfItems );
|
||||
|
||||
return pxList->uxNumberOfItems;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Implementation of functions defined in portable.h for the RISC-V port.
|
||||
*----------------------------------------------------------*/
|
||||
|
||||
/* Scheduler includes. */
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "portmacro.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include "string.h"
|
||||
|
||||
#ifdef configCLINT_BASE_ADDRESS
|
||||
#warning "The configCLINT_BASE_ADDRESS constant has been deprecated. configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS are currently being derived from the (possibly 0) configCLINT_BASE_ADDRESS setting. Please update to define configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS directly in place of configCLINT_BASE_ADDRESS. See www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html"
|
||||
#endif
|
||||
|
||||
#ifndef configMTIME_BASE_ADDRESS
|
||||
#warning "configMTIME_BASE_ADDRESS must be defined in FreeRTOSConfig.h. If the target chip includes a memory-mapped mtime register then set configMTIME_BASE_ADDRESS to the mapped address. Otherwise set configMTIME_BASE_ADDRESS to 0. See www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html"
|
||||
#endif
|
||||
|
||||
#ifndef configMTIMECMP_BASE_ADDRESS
|
||||
#warning "configMTIMECMP_BASE_ADDRESS must be defined in FreeRTOSConfig.h. If the target chip includes a memory-mapped mtimecmp register then set configMTIMECMP_BASE_ADDRESS to the mapped address. Otherwise set configMTIMECMP_BASE_ADDRESS to 0. See www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html"
|
||||
#endif
|
||||
|
||||
/* Let the user override the pre-loading of the initial RA. */
|
||||
#ifdef configTASK_RETURN_ADDRESS
|
||||
#define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS
|
||||
#else
|
||||
#define portTASK_RETURN_ADDRESS 0
|
||||
#endif
|
||||
|
||||
/* The stack used by interrupt service routines. Set configISR_STACK_SIZE_WORDS
|
||||
* to use a statically allocated array as the interrupt stack. Alternative leave
|
||||
* configISR_STACK_SIZE_WORDS undefined and update the linker script so that a
|
||||
* linker variable names __freertos_irq_stack_top has the same value as the top
|
||||
* of the stack used by main. Using the linker script method will repurpose the
|
||||
* stack that was used by main before the scheduler was started for use as the
|
||||
* interrupt stack after the scheduler has started. */
|
||||
#ifdef configISR_STACK_SIZE_WORDS
|
||||
static __attribute__( ( aligned( 16 ) ) ) StackType_t xISRStack[ configISR_STACK_SIZE_WORDS ] = { 0 };
|
||||
const StackType_t xISRStackTop = ( StackType_t ) &( xISRStack[ configISR_STACK_SIZE_WORDS & ~portBYTE_ALIGNMENT_MASK ] );
|
||||
|
||||
/* Don't use 0xa5 as the stack fill bytes as that is used by the kernel for
|
||||
* the task stacks, and so will legitimately appear in many positions within
|
||||
* the ISR stack. */
|
||||
#define portISR_STACK_FILL_BYTE 0xee
|
||||
#else
|
||||
extern const uint32_t __freertos_irq_stack_top[];
|
||||
const StackType_t xISRStackTop = ( StackType_t ) __freertos_irq_stack_top;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Setup the timer to generate the tick interrupts. The implementation in this
|
||||
* file is weak to allow application writers to change the timer used to
|
||||
* generate the tick interrupt.
|
||||
*/
|
||||
void vPortSetupTimerInterrupt( void ) __attribute__( ( weak ) );
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Used to program the machine timer compare register. */
|
||||
uint64_t ullNextTime = 0ULL;
|
||||
const uint64_t * pullNextTime = &ullNextTime;
|
||||
const size_t uxTimerIncrementsForOneTick = ( size_t ) ( ( configCPU_CLOCK_HZ ) / ( configTICK_RATE_HZ ) ); /* Assumes increment won't go over 32-bits. */
|
||||
UBaseType_t const ullMachineTimerCompareRegisterBase = configMTIMECMP_BASE_ADDRESS;
|
||||
volatile uint64_t * pullMachineTimerCompareRegister = NULL;
|
||||
|
||||
/* Holds the critical nesting value - deliberately non-zero at start up to
|
||||
* ensure interrupts are not accidentally enabled before the scheduler starts. */
|
||||
size_t xCriticalNesting = ( size_t ) 0xaaaaaaaa;
|
||||
size_t * pxCriticalNesting = &xCriticalNesting;
|
||||
|
||||
/* Used to catch tasks that attempt to return from their implementing function. */
|
||||
size_t xTaskReturnAddress = ( size_t ) portTASK_RETURN_ADDRESS;
|
||||
|
||||
/* Set configCHECK_FOR_STACK_OVERFLOW to 3 to add ISR stack checking to task
|
||||
* stack checking. A problem in the ISR stack will trigger an assert, not call
|
||||
* the stack overflow hook function (because the stack overflow hook is specific
|
||||
* to a task stack, not the ISR stack). */
|
||||
#if defined( configISR_STACK_SIZE_WORDS ) && ( configCHECK_FOR_STACK_OVERFLOW > 2 )
|
||||
#warning "This path not tested, or even compiled yet."
|
||||
|
||||
static const uint8_t ucExpectedStackBytes[] =
|
||||
{
|
||||
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
|
||||
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
|
||||
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
|
||||
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
|
||||
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE
|
||||
}; \
|
||||
|
||||
#define portCHECK_ISR_STACK() configASSERT( ( memcmp( ( void * ) xISRStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) == 0 ) )
|
||||
#else /* if defined( configISR_STACK_SIZE_WORDS ) && ( configCHECK_FOR_STACK_OVERFLOW > 2 ) */
|
||||
/* Define the function away. */
|
||||
#define portCHECK_ISR_STACK()
|
||||
#endif /* configCHECK_FOR_STACK_OVERFLOW > 2 */
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 )
|
||||
|
||||
void vPortSetupTimerInterrupt( void )
|
||||
{
|
||||
uint32_t ulCurrentTimeHigh, ulCurrentTimeLow;
|
||||
volatile uint32_t * const pulTimeHigh = ( volatile uint32_t * const ) ( ( configMTIME_BASE_ADDRESS ) + 4UL ); /* 8-byte type so high 32-bit word is 4 bytes up. */
|
||||
volatile uint32_t * const pulTimeLow = ( volatile uint32_t * const ) ( configMTIME_BASE_ADDRESS );
|
||||
volatile uint32_t ulHartId;
|
||||
|
||||
__asm volatile ( "csrr %0, mhartid" : "=r" ( ulHartId ) );
|
||||
|
||||
pullMachineTimerCompareRegister = ( volatile uint64_t * ) ( ullMachineTimerCompareRegisterBase + ( ulHartId * sizeof( uint64_t ) ) );
|
||||
|
||||
do
|
||||
{
|
||||
ulCurrentTimeHigh = *pulTimeHigh;
|
||||
ulCurrentTimeLow = *pulTimeLow;
|
||||
} while( ulCurrentTimeHigh != *pulTimeHigh );
|
||||
|
||||
ullNextTime = ( uint64_t ) ulCurrentTimeHigh;
|
||||
ullNextTime <<= 32ULL; /* High 4-byte word is 32-bits up. */
|
||||
ullNextTime |= ( uint64_t ) ulCurrentTimeLow;
|
||||
ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick;
|
||||
*pullMachineTimerCompareRegister = ullNextTime;
|
||||
|
||||
/* Prepare the time to use after the next tick interrupt. */
|
||||
ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick;
|
||||
}
|
||||
|
||||
#endif /* ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIME_BASE_ADDRESS != 0 ) */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
BaseType_t xPortStartScheduler( void )
|
||||
{
|
||||
extern void xPortStartFirstTask( void );
|
||||
|
||||
#if ( configASSERT_DEFINED == 1 )
|
||||
{
|
||||
/* Check alignment of the interrupt stack - which is the same as the
|
||||
* stack that was being used by main() prior to the scheduler being
|
||||
* started. */
|
||||
configASSERT( ( xISRStackTop & portBYTE_ALIGNMENT_MASK ) == 0 );
|
||||
|
||||
#ifdef configISR_STACK_SIZE_WORDS
|
||||
{
|
||||
memset( ( void * ) xISRStack, portISR_STACK_FILL_BYTE, sizeof( xISRStack ) );
|
||||
}
|
||||
#endif /* configISR_STACK_SIZE_WORDS */
|
||||
}
|
||||
#endif /* configASSERT_DEFINED */
|
||||
|
||||
/* If there is a CLINT then it is ok to use the default implementation
|
||||
* in this file, otherwise vPortSetupTimerInterrupt() must be implemented to
|
||||
* configure whichever clock is to be used to generate the tick interrupt. */
|
||||
vPortSetupTimerInterrupt();
|
||||
|
||||
#if ( ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) )
|
||||
{
|
||||
/* Enable mtime and external interrupts. 1<<7 for timer interrupt,
|
||||
* 1<<11 for external interrupt. _RB_ What happens here when mtime is
|
||||
* not present as with pulpino? */
|
||||
__asm volatile ( "csrs mie, %0" ::"r" ( 0x880 ) );
|
||||
}
|
||||
#endif /* ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) */
|
||||
|
||||
xPortStartFirstTask();
|
||||
|
||||
/* Should not get here as after calling xPortStartFirstTask() only tasks
|
||||
* should be executing. */
|
||||
return pdFAIL;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vPortEndScheduler( void )
|
||||
{
|
||||
/* Not implemented. */
|
||||
for( ; ; )
|
||||
{
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The FreeRTOS kernel's RISC-V port is split between the the code that is
|
||||
* common across all currently supported RISC-V chips (implementations of the
|
||||
* RISC-V ISA), and code which tailors the port to a specific RISC-V chip:
|
||||
*
|
||||
* + The code that is common to all RISC-V chips is implemented in
|
||||
* FreeRTOS\Source\portable\GCC\RISC-V\portASM.S. There is only one
|
||||
* portASM.S file because the same file is used no matter which RISC-V chip is
|
||||
* in use.
|
||||
*
|
||||
* + The code that tailors the kernel's RISC-V port to a specific RISC-V
|
||||
* chip is implemented in freertos_risc_v_chip_specific_extensions.h. There
|
||||
* is one freertos_risc_v_chip_specific_extensions.h that can be used with any
|
||||
* RISC-V chip that both includes a standard CLINT and does not add to the
|
||||
* base set of RISC-V registers. There are additional
|
||||
* freertos_risc_v_chip_specific_extensions.h files for RISC-V implementations
|
||||
* that do not include a standard CLINT or do add to the base set of RISC-V
|
||||
* registers.
|
||||
*
|
||||
* CARE MUST BE TAKEN TO INCLDUE THE CORRECT
|
||||
* freertos_risc_v_chip_specific_extensions.h HEADER FILE FOR THE CHIP
|
||||
* IN USE. To include the correct freertos_risc_v_chip_specific_extensions.h
|
||||
* header file ensure the path to the correct header file is in the assembler's
|
||||
* include path.
|
||||
*
|
||||
* This freertos_risc_v_chip_specific_extensions.h is for use on RISC-V chips
|
||||
* that include a standard CLINT and do not add to the base set of RISC-V
|
||||
* registers.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "portContext.h"
|
||||
|
||||
/* Check the freertos_risc_v_chip_specific_extensions.h and/or command line
|
||||
definitions. */
|
||||
#if defined( portasmHAS_CLINT ) && defined( portasmHAS_MTIME )
|
||||
#error The portasmHAS_CLINT constant has been deprecated. Please replace it with portasmHAS_MTIME. portasmHAS_CLINT and portasmHAS_MTIME cannot both be defined at once. See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
|
||||
#endif
|
||||
|
||||
#ifdef portasmHAS_CLINT
|
||||
#warning The portasmHAS_CLINT constant has been deprecated. Please replace it with portasmHAS_MTIME and portasmHAS_SIFIVE_CLINT. For now portasmHAS_MTIME and portasmHAS_SIFIVE_CLINT are derived from portasmHAS_CLINT. See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
|
||||
#define portasmHAS_MTIME portasmHAS_CLINT
|
||||
#define portasmHAS_SIFIVE_CLINT portasmHAS_CLINT
|
||||
#endif
|
||||
|
||||
#ifndef portasmHAS_MTIME
|
||||
#error freertos_risc_v_chip_specific_extensions.h must define portasmHAS_MTIME to either 1 (MTIME clock present) or 0 (MTIME clock not present). See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
|
||||
#endif
|
||||
|
||||
#ifndef portasmHAS_SIFIVE_CLINT
|
||||
#define portasmHAS_SIFIVE_CLINT 0
|
||||
#endif
|
||||
|
||||
.global xPortStartFirstTask
|
||||
.global pxPortInitialiseStack
|
||||
.global freertos_risc_v_trap_handler
|
||||
.global freertos_risc_v_exception_handler
|
||||
.global freertos_risc_v_interrupt_handler
|
||||
.global freertos_risc_v_mtimer_interrupt_handler
|
||||
|
||||
.extern vTaskSwitchContext
|
||||
.extern xTaskIncrementTick
|
||||
.extern pullMachineTimerCompareRegister
|
||||
.extern pullNextTime
|
||||
.extern uxTimerIncrementsForOneTick /* size_t type so 32-bit on 32-bit core and 64-bits on 64-bit core. */
|
||||
.extern xTaskReturnAddress
|
||||
|
||||
.weak freertos_risc_v_application_exception_handler
|
||||
.weak freertos_risc_v_application_interrupt_handler
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.macro portUPDATE_MTIMER_COMPARE_REGISTER
|
||||
load_x a0, pullMachineTimerCompareRegister /* Load address of compare register into a0. */
|
||||
load_x a1, pullNextTime /* Load the address of ullNextTime into a1. */
|
||||
|
||||
#if( __riscv_xlen == 32 )
|
||||
|
||||
/* Update the 64-bit mtimer compare match value in two 32-bit writes. */
|
||||
li a4, -1
|
||||
lw a2, 0(a1) /* Load the low word of ullNextTime into a2. */
|
||||
lw a3, 4(a1) /* Load the high word of ullNextTime into a3. */
|
||||
sw a4, 0(a0) /* Low word no smaller than old value to start with - will be overwritten below. */
|
||||
sw a3, 4(a0) /* Store high word of ullNextTime into compare register. No smaller than new value. */
|
||||
sw a2, 0(a0) /* Store low word of ullNextTime into compare register. */
|
||||
lw t0, uxTimerIncrementsForOneTick /* Load the value of ullTimerIncrementForOneTick into t0 (could this be optimized by storing in an array next to pullNextTime?). */
|
||||
add a4, t0, a2 /* Add the low word of ullNextTime to the timer increments for one tick (assumes timer increment for one tick fits in 32-bits). */
|
||||
sltu t1, a4, a2 /* See if the sum of low words overflowed (what about the zero case?). */
|
||||
add t2, a3, t1 /* Add overflow to high word of ullNextTime. */
|
||||
sw a4, 0(a1) /* Store new low word of ullNextTime. */
|
||||
sw t2, 4(a1) /* Store new high word of ullNextTime. */
|
||||
|
||||
#endif /* __riscv_xlen == 32 */
|
||||
|
||||
#if( __riscv_xlen == 64 )
|
||||
|
||||
/* Update the 64-bit mtimer compare match value. */
|
||||
ld t2, 0(a1) /* Load ullNextTime into t2. */
|
||||
sd t2, 0(a0) /* Store ullNextTime into compare register. */
|
||||
ld t0, uxTimerIncrementsForOneTick /* Load the value of ullTimerIncrementForOneTick into t0 (could this be optimized by storing in an array next to pullNextTime?). */
|
||||
add t4, t0, t2 /* Add ullNextTime to the timer increments for one tick. */
|
||||
sd t4, 0(a1) /* Store ullNextTime. */
|
||||
|
||||
#endif /* __riscv_xlen == 64 */
|
||||
.endm
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Unlike other ports pxPortInitialiseStack() is written in assembly code as it
|
||||
* needs access to the portasmADDITIONAL_CONTEXT_SIZE constant. The prototype
|
||||
* for the function is as per the other ports:
|
||||
* StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters );
|
||||
*
|
||||
* As per the standard RISC-V ABI pxTopOfStack is passed in in a0, pxCode in
|
||||
* a1, and pvParameters in a2. The new top of stack is passed out in a0.
|
||||
*
|
||||
* RISC-V maps registers to ABI names as follows (X1 to X31 integer registers
|
||||
* for the 'I' profile, X1 to X15 for the 'E' profile, currently I assumed).
|
||||
*
|
||||
* Register ABI Name Description Saver
|
||||
* x0 zero Hard-wired zero -
|
||||
* x1 ra Return address Caller
|
||||
* x2 sp Stack pointer Callee
|
||||
* x3 gp Global pointer -
|
||||
* x4 tp Thread pointer -
|
||||
* x5-7 t0-2 Temporaries Caller
|
||||
* x8 s0/fp Saved register/Frame pointer Callee
|
||||
* x9 s1 Saved register Callee
|
||||
* x10-11 a0-1 Function Arguments/return values Caller
|
||||
* x12-17 a2-7 Function arguments Caller
|
||||
* x18-27 s2-11 Saved registers Callee
|
||||
* x28-31 t3-6 Temporaries Caller
|
||||
*
|
||||
* The RISC-V context is saved to FreeRTOS tasks in the following stack frame,
|
||||
* where the global and thread pointers are currently assumed to be constant so
|
||||
* are not saved:
|
||||
*
|
||||
* xCriticalNesting
|
||||
* x31
|
||||
* x30
|
||||
* x29
|
||||
* x28
|
||||
* x27
|
||||
* x26
|
||||
* x25
|
||||
* x24
|
||||
* x23
|
||||
* x22
|
||||
* x21
|
||||
* x20
|
||||
* x19
|
||||
* x18
|
||||
* x17
|
||||
* x16
|
||||
* x15
|
||||
* x14
|
||||
* x13
|
||||
* x12
|
||||
* x11
|
||||
* pvParameters
|
||||
* x9
|
||||
* x8
|
||||
* x7
|
||||
* x6
|
||||
* x5
|
||||
* portTASK_RETURN_ADDRESS
|
||||
* [FPU registers (when enabled/available) go here]
|
||||
* [VPU registers (when enabled/available) go here]
|
||||
* mstatus
|
||||
* [chip specific registers go here]
|
||||
* pxCode
|
||||
*/
|
||||
pxPortInitialiseStack:
|
||||
addi a0, a0, -portWORD_SIZE /* Space for critical nesting count. */
|
||||
store_x x0, 0(a0) /* Critical nesting count starts at 0 for every task. */
|
||||
|
||||
#ifdef __riscv_32e
|
||||
addi a0, a0, -(6 * portWORD_SIZE) /* Space for registers x10-x15. */
|
||||
#else
|
||||
addi a0, a0, -(22 * portWORD_SIZE) /* Space for registers x10-x31. */
|
||||
#endif
|
||||
store_x a2, 0(a0) /* Task parameters (pvParameters parameter) goes into register x10/a0 on the stack. */
|
||||
|
||||
addi a0, a0, -(6 * portWORD_SIZE) /* Space for registers x5-x9 + taskReturnAddress (register x1). */
|
||||
load_x t0, xTaskReturnAddress
|
||||
store_x t0, 0(a0) /* Return address onto the stack. */
|
||||
|
||||
csrr t0, mstatus /* Obtain current mstatus value. */
|
||||
andi t0, t0, ~0x8 /* Ensure interrupts are disabled when the stack is restored within an ISR. Required when a task is created after the scheduler has been started, otherwise interrupts would be disabled anyway. */
|
||||
addi t1, x0, 0x188 /* Generate the value 0x1880, which are the MPIE=1 and MPP=M_Mode in mstatus. */
|
||||
slli t1, t1, 4
|
||||
or t0, t0, t1 /* Set MPIE and MPP bits in mstatus value. */
|
||||
|
||||
#if( configENABLE_FPU == 1 )
|
||||
/* Mark the FPU as clean in the mstatus value. */
|
||||
li t1, ~MSTATUS_FS_MASK
|
||||
and t0, t0, t1
|
||||
li t1, MSTATUS_FS_CLEAN
|
||||
or t0, t0, t1
|
||||
#endif
|
||||
|
||||
#if( configENABLE_VPU == 1 )
|
||||
/* Mark the VPU as clean in the mstatus value. */
|
||||
li t1, ~MSTATUS_VS_MASK
|
||||
and t0, t0, t1
|
||||
li t1, MSTATUS_VS_CLEAN
|
||||
or t0, t0, t1
|
||||
#endif
|
||||
|
||||
addi a0, a0, -portWORD_SIZE
|
||||
store_x t0, 0(a0) /* mstatus onto the stack. */
|
||||
|
||||
addi t0, x0, portasmADDITIONAL_CONTEXT_SIZE /* The number of chip specific additional registers. */
|
||||
chip_specific_stack_frame: /* First add any chip specific registers to the stack frame being created. */
|
||||
beq t0, x0, 1f /* No more chip specific registers to save. */
|
||||
addi a0, a0, -portWORD_SIZE /* Make space for chip specific register. */
|
||||
store_x x0, 0(a0) /* Give the chip specific register an initial value of zero. */
|
||||
addi t0, t0, -1 /* Decrement the count of chip specific registers remaining. */
|
||||
j chip_specific_stack_frame /* Until no more chip specific registers. */
|
||||
1:
|
||||
|
||||
addi a0, a0, -portWORD_SIZE
|
||||
store_x a1, 0(a0) /* mret value (pxCode parameter) onto the stack. */
|
||||
ret
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
xPortStartFirstTask:
|
||||
load_x sp, pxCurrentTCB /* Load pxCurrentTCB. */
|
||||
load_x sp, 0( sp ) /* Read sp from first TCB member. */
|
||||
|
||||
load_x x1, 0( sp ) /* Note for starting the scheduler the exception return address is used as the function return address. */
|
||||
|
||||
portasmRESTORE_ADDITIONAL_REGISTERS /* Defined in freertos_risc_v_chip_specific_extensions.h to restore any registers unique to the RISC-V implementation. */
|
||||
|
||||
load_x x5, 1 * portWORD_SIZE( sp ) /* Initial mstatus into x5 (t0). */
|
||||
addi x5, x5, 0x08 /* Set MIE bit so the first task starts with interrupts enabled - required as returns with ret not eret. */
|
||||
csrw mstatus, x5 /* Interrupts enabled from here! */
|
||||
|
||||
load_x x7, 5 * portWORD_SIZE( sp ) /* t2 */
|
||||
load_x x8, 6 * portWORD_SIZE( sp ) /* s0/fp */
|
||||
load_x x9, 7 * portWORD_SIZE( sp ) /* s1 */
|
||||
load_x x10, 8 * portWORD_SIZE( sp ) /* a0 */
|
||||
load_x x11, 9 * portWORD_SIZE( sp ) /* a1 */
|
||||
load_x x12, 10 * portWORD_SIZE( sp ) /* a2 */
|
||||
load_x x13, 11 * portWORD_SIZE( sp ) /* a3 */
|
||||
load_x x14, 12 * portWORD_SIZE( sp ) /* a4 */
|
||||
load_x x15, 13 * portWORD_SIZE( sp ) /* a5 */
|
||||
#ifndef __riscv_32e
|
||||
load_x x16, 14 * portWORD_SIZE( sp ) /* a6 */
|
||||
load_x x17, 15 * portWORD_SIZE( sp ) /* a7 */
|
||||
load_x x18, 16 * portWORD_SIZE( sp ) /* s2 */
|
||||
load_x x19, 17 * portWORD_SIZE( sp ) /* s3 */
|
||||
load_x x20, 18 * portWORD_SIZE( sp ) /* s4 */
|
||||
load_x x21, 19 * portWORD_SIZE( sp ) /* s5 */
|
||||
load_x x22, 20 * portWORD_SIZE( sp ) /* s6 */
|
||||
load_x x23, 21 * portWORD_SIZE( sp ) /* s7 */
|
||||
load_x x24, 22 * portWORD_SIZE( sp ) /* s8 */
|
||||
load_x x25, 23 * portWORD_SIZE( sp ) /* s9 */
|
||||
load_x x26, 24 * portWORD_SIZE( sp ) /* s10 */
|
||||
load_x x27, 25 * portWORD_SIZE( sp ) /* s11 */
|
||||
load_x x28, 26 * portWORD_SIZE( sp ) /* t3 */
|
||||
load_x x29, 27 * portWORD_SIZE( sp ) /* t4 */
|
||||
load_x x30, 28 * portWORD_SIZE( sp ) /* t5 */
|
||||
load_x x31, 29 * portWORD_SIZE( sp ) /* t6 */
|
||||
#endif
|
||||
|
||||
load_x x5, portCRITICAL_NESTING_OFFSET * portWORD_SIZE( sp ) /* Obtain xCriticalNesting value for this task from task's stack. */
|
||||
load_x x6, pxCriticalNesting /* Load the address of xCriticalNesting into x6. */
|
||||
store_x x5, 0( x6 ) /* Restore the critical nesting value for this task. */
|
||||
|
||||
load_x x5, 3 * portWORD_SIZE( sp ) /* Initial x5 (t0) value. */
|
||||
load_x x6, 4 * portWORD_SIZE( sp ) /* Initial x6 (t1) value. */
|
||||
|
||||
addi sp, sp, portCONTEXT_SIZE
|
||||
ret
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
freertos_risc_v_application_exception_handler:
|
||||
csrr t0, mcause /* For viewing in the debugger only. */
|
||||
csrr t1, mepc /* For viewing in the debugger only */
|
||||
csrr t2, mstatus /* For viewing in the debugger only */
|
||||
j .
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
freertos_risc_v_application_interrupt_handler:
|
||||
csrr t0, mcause /* For viewing in the debugger only. */
|
||||
csrr t1, mepc /* For viewing in the debugger only */
|
||||
csrr t2, mstatus /* For viewing in the debugger only */
|
||||
j .
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.section .text.freertos_risc_v_exception_handler
|
||||
freertos_risc_v_exception_handler:
|
||||
portcontextSAVE_EXCEPTION_CONTEXT
|
||||
/* a0 now contains mcause. */
|
||||
li t0, 11 /* 11 == environment call. */
|
||||
bne a0, t0, other_exception /* Not an M environment call, so some other exception. */
|
||||
call vTaskSwitchContext
|
||||
portcontextRESTORE_CONTEXT
|
||||
|
||||
other_exception:
|
||||
call freertos_risc_v_application_exception_handler
|
||||
portcontextRESTORE_CONTEXT
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.section .text.freertos_risc_v_interrupt_handler
|
||||
freertos_risc_v_interrupt_handler:
|
||||
portcontextSAVE_INTERRUPT_CONTEXT
|
||||
call freertos_risc_v_application_interrupt_handler
|
||||
portcontextRESTORE_CONTEXT
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.section .text.freertos_risc_v_mtimer_interrupt_handler
|
||||
freertos_risc_v_mtimer_interrupt_handler:
|
||||
portcontextSAVE_INTERRUPT_CONTEXT
|
||||
portUPDATE_MTIMER_COMPARE_REGISTER
|
||||
call xTaskIncrementTick
|
||||
beqz a0, exit_without_context_switch /* Don't switch context if incrementing tick didn't unblock a task. */
|
||||
call vTaskSwitchContext
|
||||
exit_without_context_switch:
|
||||
portcontextRESTORE_CONTEXT
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.section .text.freertos_risc_v_trap_handler
|
||||
.align 8
|
||||
freertos_risc_v_trap_handler:
|
||||
portcontextSAVE_CONTEXT_INTERNAL
|
||||
|
||||
csrr a0, mcause
|
||||
csrr a1, mepc
|
||||
|
||||
bge a0, x0, synchronous_exception
|
||||
|
||||
asynchronous_interrupt:
|
||||
store_x a1, 0( sp ) /* Asynchronous interrupt so save unmodified exception return address. */
|
||||
load_x sp, xISRStackTop /* Switch to ISR stack. */
|
||||
j handle_interrupt
|
||||
|
||||
synchronous_exception:
|
||||
addi a1, a1, 4 /* Synchronous so update exception return address to the instruction after the instruction that generated the exeption. */
|
||||
store_x a1, 0( sp ) /* Save updated exception return address. */
|
||||
load_x sp, xISRStackTop /* Switch to ISR stack. */
|
||||
j handle_exception
|
||||
|
||||
handle_interrupt:
|
||||
#if( portasmHAS_MTIME != 0 )
|
||||
|
||||
test_if_mtimer: /* If there is a CLINT then the mtimer is used to generate the tick interrupt. */
|
||||
addi t0, x0, 1
|
||||
slli t0, t0, __riscv_xlen - 1 /* LSB is already set, shift into MSB. Shift 31 on 32-bit or 63 on 64-bit cores. */
|
||||
addi t1, t0, 7 /* 0x8000[]0007 == machine timer interrupt. */
|
||||
bne a0, t1, application_interrupt_handler
|
||||
|
||||
portUPDATE_MTIMER_COMPARE_REGISTER
|
||||
call xTaskIncrementTick
|
||||
beqz a0, processed_source /* Don't switch context if incrementing tick didn't unblock a task. */
|
||||
call vTaskSwitchContext
|
||||
j processed_source
|
||||
|
||||
#endif /* portasmHAS_MTIME */
|
||||
|
||||
application_interrupt_handler:
|
||||
call freertos_risc_v_application_interrupt_handler
|
||||
j processed_source
|
||||
|
||||
handle_exception:
|
||||
/* a0 contains mcause. */
|
||||
li t0, 11 /* 11 == environment call. */
|
||||
bne a0, t0, application_exception_handler /* Not an M environment call, so some other exception. */
|
||||
call vTaskSwitchContext
|
||||
j processed_source
|
||||
|
||||
application_exception_handler:
|
||||
call freertos_risc_v_application_exception_handler
|
||||
j processed_source /* No other exceptions handled yet. */
|
||||
|
||||
processed_source:
|
||||
portcontextRESTORE_CONTEXT
|
||||
/*-----------------------------------------------------------*/
|
||||
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PORTCONTEXT_H
|
||||
#define PORTCONTEXT_H
|
||||
|
||||
#ifndef configENABLE_FPU
|
||||
#define configENABLE_FPU 0
|
||||
#endif
|
||||
|
||||
#ifndef configENABLE_VPU
|
||||
#define configENABLE_VPU 0
|
||||
#endif
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
#define portWORD_SIZE 8
|
||||
#define store_x sd
|
||||
#define load_x ld
|
||||
#elif __riscv_xlen == 32
|
||||
#define store_x sw
|
||||
#define load_x lw
|
||||
#define portWORD_SIZE 4
|
||||
#else
|
||||
#error Assembler did not define __riscv_xlen
|
||||
#endif
|
||||
|
||||
#include "freertos_risc_v_chip_specific_extensions.h"
|
||||
|
||||
/* Only the standard core registers are stored by default. Any additional
|
||||
* registers must be saved by the portasmSAVE_ADDITIONAL_REGISTERS and
|
||||
* portasmRESTORE_ADDITIONAL_REGISTERS macros - which can be defined in a chip
|
||||
* specific version of freertos_risc_v_chip_specific_extensions.h. See the
|
||||
* notes at the top of portASM.S file. */
|
||||
#ifdef __riscv_32e
|
||||
#define portCONTEXT_SIZE ( 15 * portWORD_SIZE )
|
||||
#define portCRITICAL_NESTING_OFFSET 14
|
||||
#else
|
||||
#define portCONTEXT_SIZE ( 31 * portWORD_SIZE )
|
||||
#define portCRITICAL_NESTING_OFFSET 30
|
||||
#endif
|
||||
|
||||
#if ( configENABLE_FPU == 1 )
|
||||
/* Bit [14:13] in the mstatus encode the status of FPU state which is one of
|
||||
* the following values:
|
||||
* 1. Value: 0, Meaning: Off.
|
||||
* 2. Value: 1, Meaning: Initial.
|
||||
* 3. Value: 2, Meaning: Clean.
|
||||
* 4. Value: 3, Meaning: Dirty.
|
||||
*/
|
||||
#define MSTATUS_FS_MASK 0x6000
|
||||
#define MSTATUS_FS_INITIAL 0x2000
|
||||
#define MSTATUS_FS_CLEAN 0x4000
|
||||
#define MSTATUS_FS_DIRTY 0x6000
|
||||
#define MSTATUS_FS_OFFSET 13
|
||||
|
||||
#ifdef __riscv_fdiv
|
||||
#if __riscv_flen == 32
|
||||
#define load_f flw
|
||||
#define store_f fsw
|
||||
#elif __riscv_flen == 64
|
||||
#define load_f fld
|
||||
#define store_f fsd
|
||||
#else
|
||||
#error Assembler did not define __riscv_flen
|
||||
#endif
|
||||
|
||||
#define portFPU_REG_SIZE ( __riscv_flen / 8 )
|
||||
#define portFPU_REG_COUNT 33 /* 32 Floating point registers plus one CSR. */
|
||||
#define portFPU_REG_OFFSET( regIndex ) ( ( 2 * portWORD_SIZE ) + ( regIndex * portFPU_REG_SIZE ) )
|
||||
#define portFPU_CONTEXT_SIZE ( portFPU_REG_SIZE * portFPU_REG_COUNT )
|
||||
#else
|
||||
#error configENABLE_FPU must not be set to 1 if the hardware does not have FPU
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ( configENABLE_VPU == 1 )
|
||||
/* Bit [10:9] in the mstatus encode the status of VPU state which is one of
|
||||
* the following values:
|
||||
* 1. Value: 0, Meaning: Off.
|
||||
* 2. Value: 1, Meaning: Initial.
|
||||
* 3. Value: 2, Meaning: Clean.
|
||||
* 4. Value: 3, Meaning: Dirty.
|
||||
*/
|
||||
#define MSTATUS_VS_MASK 0x600
|
||||
#define MSTATUS_VS_INITIAL 0x200
|
||||
#define MSTATUS_VS_CLEAN 0x400
|
||||
#define MSTATUS_VS_DIRTY 0x600
|
||||
#define MSTATUS_VS_OFFSET 9
|
||||
|
||||
#ifndef __riscv_vector
|
||||
#error configENABLE_VPU must not be set to 1 if the hardware does not have VPU
|
||||
#endif
|
||||
#endif
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.extern pxCurrentTCB
|
||||
.extern xISRStackTop
|
||||
.extern xCriticalNesting
|
||||
.extern pxCriticalNesting
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.macro portcontexSAVE_FPU_CONTEXT
|
||||
addi sp, sp, -( portFPU_CONTEXT_SIZE )
|
||||
/* Store the FPU registers. */
|
||||
store_f f0, portFPU_REG_OFFSET( 0 )( sp )
|
||||
store_f f1, portFPU_REG_OFFSET( 1 )( sp )
|
||||
store_f f2, portFPU_REG_OFFSET( 2 )( sp )
|
||||
store_f f3, portFPU_REG_OFFSET( 3 )( sp )
|
||||
store_f f4, portFPU_REG_OFFSET( 4 )( sp )
|
||||
store_f f5, portFPU_REG_OFFSET( 5 )( sp )
|
||||
store_f f6, portFPU_REG_OFFSET( 6 )( sp )
|
||||
store_f f7, portFPU_REG_OFFSET( 7 )( sp )
|
||||
store_f f8, portFPU_REG_OFFSET( 8 )( sp )
|
||||
store_f f9, portFPU_REG_OFFSET( 9 )( sp )
|
||||
store_f f10, portFPU_REG_OFFSET( 10 )( sp )
|
||||
store_f f11, portFPU_REG_OFFSET( 11 )( sp )
|
||||
store_f f12, portFPU_REG_OFFSET( 12 )( sp )
|
||||
store_f f13, portFPU_REG_OFFSET( 13 )( sp )
|
||||
store_f f14, portFPU_REG_OFFSET( 14 )( sp )
|
||||
store_f f15, portFPU_REG_OFFSET( 15 )( sp )
|
||||
store_f f16, portFPU_REG_OFFSET( 16 )( sp )
|
||||
store_f f17, portFPU_REG_OFFSET( 17 )( sp )
|
||||
store_f f18, portFPU_REG_OFFSET( 18 )( sp )
|
||||
store_f f19, portFPU_REG_OFFSET( 19 )( sp )
|
||||
store_f f20, portFPU_REG_OFFSET( 20 )( sp )
|
||||
store_f f21, portFPU_REG_OFFSET( 21 )( sp )
|
||||
store_f f22, portFPU_REG_OFFSET( 22 )( sp )
|
||||
store_f f23, portFPU_REG_OFFSET( 23 )( sp )
|
||||
store_f f24, portFPU_REG_OFFSET( 24 )( sp )
|
||||
store_f f25, portFPU_REG_OFFSET( 25 )( sp )
|
||||
store_f f26, portFPU_REG_OFFSET( 26 )( sp )
|
||||
store_f f27, portFPU_REG_OFFSET( 27 )( sp )
|
||||
store_f f28, portFPU_REG_OFFSET( 28 )( sp )
|
||||
store_f f29, portFPU_REG_OFFSET( 29 )( sp )
|
||||
store_f f30, portFPU_REG_OFFSET( 30 )( sp )
|
||||
store_f f31, portFPU_REG_OFFSET( 31 )( sp )
|
||||
csrr t0, fcsr
|
||||
store_x t0, portFPU_REG_OFFSET( 32 )( sp )
|
||||
.endm
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.macro portcontextRESTORE_FPU_CONTEXT
|
||||
/* Restore the FPU registers. */
|
||||
load_f f0, portFPU_REG_OFFSET( 0 )( sp )
|
||||
load_f f1, portFPU_REG_OFFSET( 1 )( sp )
|
||||
load_f f2, portFPU_REG_OFFSET( 2 )( sp )
|
||||
load_f f3, portFPU_REG_OFFSET( 3 )( sp )
|
||||
load_f f4, portFPU_REG_OFFSET( 4 )( sp )
|
||||
load_f f5, portFPU_REG_OFFSET( 5 )( sp )
|
||||
load_f f6, portFPU_REG_OFFSET( 6 )( sp )
|
||||
load_f f7, portFPU_REG_OFFSET( 7 )( sp )
|
||||
load_f f8, portFPU_REG_OFFSET( 8 )( sp )
|
||||
load_f f9, portFPU_REG_OFFSET( 9 )( sp )
|
||||
load_f f10, portFPU_REG_OFFSET( 10 )( sp )
|
||||
load_f f11, portFPU_REG_OFFSET( 11 )( sp )
|
||||
load_f f12, portFPU_REG_OFFSET( 12 )( sp )
|
||||
load_f f13, portFPU_REG_OFFSET( 13 )( sp )
|
||||
load_f f14, portFPU_REG_OFFSET( 14 )( sp )
|
||||
load_f f15, portFPU_REG_OFFSET( 15 )( sp )
|
||||
load_f f16, portFPU_REG_OFFSET( 16 )( sp )
|
||||
load_f f17, portFPU_REG_OFFSET( 17 )( sp )
|
||||
load_f f18, portFPU_REG_OFFSET( 18 )( sp )
|
||||
load_f f19, portFPU_REG_OFFSET( 19 )( sp )
|
||||
load_f f20, portFPU_REG_OFFSET( 20 )( sp )
|
||||
load_f f21, portFPU_REG_OFFSET( 21 )( sp )
|
||||
load_f f22, portFPU_REG_OFFSET( 22 )( sp )
|
||||
load_f f23, portFPU_REG_OFFSET( 23 )( sp )
|
||||
load_f f24, portFPU_REG_OFFSET( 24 )( sp )
|
||||
load_f f25, portFPU_REG_OFFSET( 25 )( sp )
|
||||
load_f f26, portFPU_REG_OFFSET( 26 )( sp )
|
||||
load_f f27, portFPU_REG_OFFSET( 27 )( sp )
|
||||
load_f f28, portFPU_REG_OFFSET( 28 )( sp )
|
||||
load_f f29, portFPU_REG_OFFSET( 29 )( sp )
|
||||
load_f f30, portFPU_REG_OFFSET( 30 )( sp )
|
||||
load_f f31, portFPU_REG_OFFSET( 31 )( sp )
|
||||
load_x t0, portFPU_REG_OFFSET( 32 )( sp )
|
||||
csrw fcsr, t0
|
||||
addi sp, sp, ( portFPU_CONTEXT_SIZE )
|
||||
.endm
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.macro portcontexSAVE_VPU_CONTEXT
|
||||
/* Un-reserve the space reserved for mstatus and epc. */
|
||||
add sp, sp, ( 2 * portWORD_SIZE )
|
||||
|
||||
csrr t0, vlenb /* t0 = vlenb. vlenb is the length of each vector register in bytes. */
|
||||
slli t0, t0, 3 /* t0 = vlenb * 8. t0 now contains the space required to store 8 vector registers. */
|
||||
neg t0, t0
|
||||
|
||||
/* Store the vector registers in group of 8. */
|
||||
add sp, sp, t0
|
||||
vs8r.v v24, (sp) /* Store v24-v31. */
|
||||
add sp, sp, t0
|
||||
vs8r.v v16, (sp) /* Store v16-v23. */
|
||||
add sp, sp, t0
|
||||
vs8r.v v8, (sp) /* Store v8-v15. */
|
||||
add sp, sp, t0
|
||||
vs8r.v v0, (sp) /* Store v0-v7. */
|
||||
|
||||
/* Store the VPU CSRs. */
|
||||
addi sp, sp, -( 4 * portWORD_SIZE )
|
||||
csrr t0, vstart
|
||||
store_x t0, 0 * portWORD_SIZE( sp )
|
||||
csrr t0, vcsr
|
||||
store_x t0, 1 * portWORD_SIZE( sp )
|
||||
csrr t0, vl
|
||||
store_x t0, 2 * portWORD_SIZE( sp )
|
||||
csrr t0, vtype
|
||||
store_x t0, 3 * portWORD_SIZE( sp )
|
||||
|
||||
/* Re-reserve the space for mstatus and epc. */
|
||||
add sp, sp, -( 2 * portWORD_SIZE )
|
||||
.endm
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.macro portcontextRESTORE_VPU_CONTEXT
|
||||
/* Un-reserve the space reserved for mstatus and epc. */
|
||||
add sp, sp, ( 2 * portWORD_SIZE )
|
||||
|
||||
/* Restore the VPU CSRs. */
|
||||
load_x t0, 0 * portWORD_SIZE( sp )
|
||||
csrw vstart, t0
|
||||
load_x t0, 1 * portWORD_SIZE( sp )
|
||||
csrw vcsr, t0
|
||||
load_x t0, 2 * portWORD_SIZE( sp )
|
||||
load_x t1, 3 * portWORD_SIZE( sp )
|
||||
vsetvl x0, t0, t1 /* vlen and vtype can only be updated by using vset*vl* instructions. */
|
||||
addi sp, sp, ( 4 * portWORD_SIZE )
|
||||
|
||||
csrr t0, vlenb /* t0 = vlenb. vlenb is the length of each vector register in bytes. */
|
||||
slli t0, t0, 3 /* t0 = vlenb * 8. t0 now contains the space required to store 8 vector registers. */
|
||||
|
||||
/* Restore the vector registers. */
|
||||
vl8r.v v0, (sp) /* Restore v0-v7. */
|
||||
add sp, sp, t0
|
||||
vl8r.v v8, (sp) /* Restore v8-v15. */
|
||||
add sp, sp, t0
|
||||
vl8r.v v16, (sp) /* Restore v16-v23. */
|
||||
add sp, sp, t0
|
||||
vl8r.v v24, (sp) /* Restore v23-v31. */
|
||||
add sp, sp, t0
|
||||
|
||||
/* Re-reserve the space for mstatus and epc. */
|
||||
add sp, sp, -( 2 * portWORD_SIZE )
|
||||
.endm
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.macro portcontextSAVE_CONTEXT_INTERNAL
|
||||
addi sp, sp, -portCONTEXT_SIZE
|
||||
store_x x1, 2 * portWORD_SIZE( sp )
|
||||
store_x x5, 3 * portWORD_SIZE( sp )
|
||||
store_x x6, 4 * portWORD_SIZE( sp )
|
||||
store_x x7, 5 * portWORD_SIZE( sp )
|
||||
store_x x8, 6 * portWORD_SIZE( sp )
|
||||
store_x x9, 7 * portWORD_SIZE( sp )
|
||||
store_x x10, 8 * portWORD_SIZE( sp )
|
||||
store_x x11, 9 * portWORD_SIZE( sp )
|
||||
store_x x12, 10 * portWORD_SIZE( sp )
|
||||
store_x x13, 11 * portWORD_SIZE( sp )
|
||||
store_x x14, 12 * portWORD_SIZE( sp )
|
||||
store_x x15, 13 * portWORD_SIZE( sp )
|
||||
#ifndef __riscv_32e
|
||||
store_x x16, 14 * portWORD_SIZE( sp )
|
||||
store_x x17, 15 * portWORD_SIZE( sp )
|
||||
store_x x18, 16 * portWORD_SIZE( sp )
|
||||
store_x x19, 17 * portWORD_SIZE( sp )
|
||||
store_x x20, 18 * portWORD_SIZE( sp )
|
||||
store_x x21, 19 * portWORD_SIZE( sp )
|
||||
store_x x22, 20 * portWORD_SIZE( sp )
|
||||
store_x x23, 21 * portWORD_SIZE( sp )
|
||||
store_x x24, 22 * portWORD_SIZE( sp )
|
||||
store_x x25, 23 * portWORD_SIZE( sp )
|
||||
store_x x26, 24 * portWORD_SIZE( sp )
|
||||
store_x x27, 25 * portWORD_SIZE( sp )
|
||||
store_x x28, 26 * portWORD_SIZE( sp )
|
||||
store_x x29, 27 * portWORD_SIZE( sp )
|
||||
store_x x30, 28 * portWORD_SIZE( sp )
|
||||
store_x x31, 29 * portWORD_SIZE( sp )
|
||||
#endif /* ifndef __riscv_32e */
|
||||
|
||||
load_x t0, xCriticalNesting /* Load the value of xCriticalNesting into t0. */
|
||||
store_x t0, portCRITICAL_NESTING_OFFSET * portWORD_SIZE( sp ) /* Store the critical nesting value to the stack. */
|
||||
|
||||
#if( configENABLE_FPU == 1 )
|
||||
csrr t0, mstatus
|
||||
srl t1, t0, MSTATUS_FS_OFFSET
|
||||
andi t1, t1, 3
|
||||
addi t2, x0, 3
|
||||
bne t1, t2, 1f /* If FPU status is not dirty, do not save FPU registers. */
|
||||
|
||||
portcontexSAVE_FPU_CONTEXT
|
||||
1:
|
||||
#endif
|
||||
|
||||
#if( configENABLE_VPU == 1 )
|
||||
csrr t0, mstatus
|
||||
srl t1, t0, MSTATUS_VS_OFFSET
|
||||
andi t1, t1, 3
|
||||
addi t2, x0, 3
|
||||
bne t1, t2, 2f /* If VPU status is not dirty, do not save VPU registers. */
|
||||
|
||||
portcontexSAVE_VPU_CONTEXT
|
||||
2:
|
||||
#endif
|
||||
|
||||
csrr t0, mstatus
|
||||
store_x t0, 1 * portWORD_SIZE( sp )
|
||||
|
||||
portasmSAVE_ADDITIONAL_REGISTERS /* Defined in freertos_risc_v_chip_specific_extensions.h to save any registers unique to the RISC-V implementation. */
|
||||
|
||||
#if( configENABLE_FPU == 1 )
|
||||
/* Mark the FPU as clean, if it was dirty and we saved FPU registers. */
|
||||
srl t1, t0, MSTATUS_FS_OFFSET
|
||||
andi t1, t1, 3
|
||||
addi t2, x0, 3
|
||||
bne t1, t2, 3f
|
||||
|
||||
li t1, ~MSTATUS_FS_MASK
|
||||
and t0, t0, t1
|
||||
li t1, MSTATUS_FS_CLEAN
|
||||
or t0, t0, t1
|
||||
csrw mstatus, t0
|
||||
3:
|
||||
#endif
|
||||
|
||||
#if( configENABLE_VPU == 1 )
|
||||
/* Mark the VPU as clean, if it was dirty and we saved VPU registers. */
|
||||
srl t1, t0, MSTATUS_VS_OFFSET
|
||||
andi t1, t1, 3
|
||||
addi t2, x0, 3
|
||||
bne t1, t2, 4f
|
||||
|
||||
li t1, ~MSTATUS_VS_MASK
|
||||
and t0, t0, t1
|
||||
li t1, MSTATUS_VS_CLEAN
|
||||
or t0, t0, t1
|
||||
csrw mstatus, t0
|
||||
4:
|
||||
#endif
|
||||
|
||||
load_x t0, pxCurrentTCB /* Load pxCurrentTCB. */
|
||||
store_x sp, 0 ( t0 ) /* Write sp to first TCB member. */
|
||||
|
||||
.endm
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.macro portcontextSAVE_EXCEPTION_CONTEXT
|
||||
portcontextSAVE_CONTEXT_INTERNAL
|
||||
csrr a0, mcause
|
||||
csrr a1, mepc
|
||||
addi a1, a1, 4 /* Synchronous so update exception return address to the instruction after the instruction that generated the exception. */
|
||||
store_x a1, 0 ( sp ) /* Save updated exception return address. */
|
||||
load_x sp, xISRStackTop /* Switch to ISR stack. */
|
||||
.endm
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.macro portcontextSAVE_INTERRUPT_CONTEXT
|
||||
portcontextSAVE_CONTEXT_INTERNAL
|
||||
csrr a0, mcause
|
||||
csrr a1, mepc
|
||||
store_x a1, 0 ( sp ) /* Asynchronous interrupt so save unmodified exception return address. */
|
||||
load_x sp, xISRStackTop /* Switch to ISR stack. */
|
||||
.endm
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
.macro portcontextRESTORE_CONTEXT
|
||||
load_x t1, pxCurrentTCB /* Load pxCurrentTCB. */
|
||||
load_x sp, 0 ( t1 ) /* Read sp from first TCB member. */
|
||||
|
||||
/* Load mepc with the address of the instruction in the task to run next. */
|
||||
load_x t0, 0 ( sp )
|
||||
csrw mepc, t0
|
||||
|
||||
/* Defined in freertos_risc_v_chip_specific_extensions.h to restore any registers unique to the RISC-V implementation. */
|
||||
portasmRESTORE_ADDITIONAL_REGISTERS
|
||||
|
||||
/* Restore mstatus register. It is important to use t3 (and not t0) here as t3
|
||||
* is not clobbered by portcontextRESTORE_VPU_CONTEXT and
|
||||
* portcontextRESTORE_FPU_CONTEXT. */
|
||||
load_x t3, 1 * portWORD_SIZE( sp )
|
||||
csrw mstatus, t3
|
||||
|
||||
#if( configENABLE_VPU == 1 )
|
||||
srl t1, t3, MSTATUS_VS_OFFSET
|
||||
andi t1, t1, 3
|
||||
addi t2, x0, 3
|
||||
bne t1, t2, 5f /* If VPU status is not dirty, do not restore VPU registers. */
|
||||
|
||||
portcontextRESTORE_VPU_CONTEXT
|
||||
5:
|
||||
#endif /* ifdef portasmSTORE_VPU_CONTEXT */
|
||||
|
||||
#if( configENABLE_FPU == 1 )
|
||||
srl t1, t3, MSTATUS_FS_OFFSET
|
||||
andi t1, t1, 3
|
||||
addi t2, x0, 3
|
||||
bne t1, t2, 6f /* If FPU status is not dirty, do not restore FPU registers. */
|
||||
|
||||
portcontextRESTORE_FPU_CONTEXT
|
||||
6:
|
||||
#endif /* ifdef portasmSTORE_FPU_CONTEXT */
|
||||
|
||||
load_x t0, portCRITICAL_NESTING_OFFSET * portWORD_SIZE( sp ) /* Obtain xCriticalNesting value for this task from task's stack. */
|
||||
load_x t1, pxCriticalNesting /* Load the address of xCriticalNesting into t1. */
|
||||
store_x t0, 0 ( t1 ) /* Restore the critical nesting value for this task. */
|
||||
|
||||
load_x x1, 2 * portWORD_SIZE( sp )
|
||||
load_x x5, 3 * portWORD_SIZE( sp )
|
||||
load_x x6, 4 * portWORD_SIZE( sp )
|
||||
load_x x7, 5 * portWORD_SIZE( sp )
|
||||
load_x x8, 6 * portWORD_SIZE( sp )
|
||||
load_x x9, 7 * portWORD_SIZE( sp )
|
||||
load_x x10, 8 * portWORD_SIZE( sp )
|
||||
load_x x11, 9 * portWORD_SIZE( sp )
|
||||
load_x x12, 10 * portWORD_SIZE( sp )
|
||||
load_x x13, 11 * portWORD_SIZE( sp )
|
||||
load_x x14, 12 * portWORD_SIZE( sp )
|
||||
load_x x15, 13 * portWORD_SIZE( sp )
|
||||
#ifndef __riscv_32e
|
||||
load_x x16, 14 * portWORD_SIZE( sp )
|
||||
load_x x17, 15 * portWORD_SIZE( sp )
|
||||
load_x x18, 16 * portWORD_SIZE( sp )
|
||||
load_x x19, 17 * portWORD_SIZE( sp )
|
||||
load_x x20, 18 * portWORD_SIZE( sp )
|
||||
load_x x21, 19 * portWORD_SIZE( sp )
|
||||
load_x x22, 20 * portWORD_SIZE( sp )
|
||||
load_x x23, 21 * portWORD_SIZE( sp )
|
||||
load_x x24, 22 * portWORD_SIZE( sp )
|
||||
load_x x25, 23 * portWORD_SIZE( sp )
|
||||
load_x x26, 24 * portWORD_SIZE( sp )
|
||||
load_x x27, 25 * portWORD_SIZE( sp )
|
||||
load_x x28, 26 * portWORD_SIZE( sp )
|
||||
load_x x29, 27 * portWORD_SIZE( sp )
|
||||
load_x x30, 28 * portWORD_SIZE( sp )
|
||||
load_x x31, 29 * portWORD_SIZE( sp )
|
||||
#endif /* ifndef __riscv_32e */
|
||||
addi sp, sp, portCONTEXT_SIZE
|
||||
|
||||
mret
|
||||
.endm
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#endif /* PORTCONTEXT_H */
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PORTMACRO_H
|
||||
#define PORTMACRO_H
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Port specific definitions.
|
||||
*
|
||||
* The settings in this file configure FreeRTOS correctly for the
|
||||
* given hardware and compiler.
|
||||
*
|
||||
* These settings should not be altered.
|
||||
*-----------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* Type definitions. */
|
||||
#if __riscv_xlen == 64
|
||||
#define portSTACK_TYPE uint64_t
|
||||
#define portBASE_TYPE int64_t
|
||||
#define portUBASE_TYPE uint64_t
|
||||
#define portMAX_DELAY ( TickType_t ) 0xffffffffffffffffUL
|
||||
#define portPOINTER_SIZE_TYPE uint64_t
|
||||
#elif __riscv_xlen == 32
|
||||
#define portSTACK_TYPE uint32_t
|
||||
#define portBASE_TYPE int32_t
|
||||
#define portUBASE_TYPE uint32_t
|
||||
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
|
||||
#else /* if __riscv_xlen == 64 */
|
||||
#error "Assembler did not define __riscv_xlen"
|
||||
#endif /* if __riscv_xlen == 64 */
|
||||
|
||||
typedef portSTACK_TYPE StackType_t;
|
||||
typedef portBASE_TYPE BaseType_t;
|
||||
typedef portUBASE_TYPE UBaseType_t;
|
||||
typedef portUBASE_TYPE TickType_t;
|
||||
|
||||
/* Legacy type definitions. */
|
||||
#define portCHAR char
|
||||
#define portFLOAT float
|
||||
#define portDOUBLE double
|
||||
#define portLONG long
|
||||
#define portSHORT short
|
||||
|
||||
/* 32-bit tick type on a 32-bit architecture, so reads of the tick count do
|
||||
* not need to be guarded with a critical section. */
|
||||
#define portTICK_TYPE_IS_ATOMIC 1
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Architecture specifics. */
|
||||
#define portSTACK_GROWTH ( -1 )
|
||||
#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
|
||||
#ifdef __riscv_32e
|
||||
#define portBYTE_ALIGNMENT 8 /* RV32E uses RISC-V EABI with reduced stack alignment requirements */
|
||||
#else
|
||||
#define portBYTE_ALIGNMENT 16
|
||||
#endif
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Scheduler utilities. */
|
||||
extern void vTaskSwitchContext( void );
|
||||
#define portYIELD() __asm volatile ( "ecall" );
|
||||
#define portEND_SWITCHING_ISR( xSwitchRequired ) \
|
||||
do \
|
||||
{ \
|
||||
if( xSwitchRequired != pdFALSE ) \
|
||||
{ \
|
||||
traceISR_EXIT_TO_SCHEDULER(); \
|
||||
vTaskSwitchContext(); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
traceISR_EXIT(); \
|
||||
} \
|
||||
} while( 0 )
|
||||
#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Critical section management. */
|
||||
#define portCRITICAL_NESTING_IN_TCB 0
|
||||
|
||||
#define portDISABLE_INTERRUPTS() __asm volatile ( "csrc mstatus, 8" )
|
||||
#define portENABLE_INTERRUPTS() __asm volatile ( "csrs mstatus, 8" )
|
||||
|
||||
extern size_t xCriticalNesting;
|
||||
#define portENTER_CRITICAL() \
|
||||
{ \
|
||||
portDISABLE_INTERRUPTS(); \
|
||||
xCriticalNesting++; \
|
||||
}
|
||||
|
||||
#define portEXIT_CRITICAL() \
|
||||
{ \
|
||||
xCriticalNesting--; \
|
||||
if( xCriticalNesting == 0 ) \
|
||||
{ \
|
||||
portENABLE_INTERRUPTS(); \
|
||||
} \
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Architecture specific optimisations. */
|
||||
#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION
|
||||
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
|
||||
#endif
|
||||
|
||||
#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 )
|
||||
|
||||
/* Check the configuration. */
|
||||
#if ( configMAX_PRIORITIES > 32 )
|
||||
#error "configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice."
|
||||
#endif
|
||||
|
||||
/* Store/clear the ready priorities in a bit map. */
|
||||
#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )
|
||||
#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - __builtin_clz( uxReadyPriorities ) )
|
||||
|
||||
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
|
||||
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Task function macros as described on the FreeRTOS.org WEB site. These are
|
||||
* not necessary for to use this port. They are defined so the common demo
|
||||
* files (which build with all the ports) will build. */
|
||||
#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void * pvParameters )
|
||||
#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void * pvParameters )
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#define portNOP() __asm volatile ( " nop " )
|
||||
#define portINLINE __inline
|
||||
|
||||
#ifndef portFORCE_INLINE
|
||||
#define portFORCE_INLINE inline __attribute__( ( always_inline ) )
|
||||
#endif
|
||||
|
||||
#define portMEMORY_BARRIER() __asm volatile ( "" ::: "memory" )
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* configCLINT_BASE_ADDRESS is a legacy definition that was replaced by the
|
||||
* configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS definitions. For
|
||||
* backward compatibility derive the newer definitions from the old if the old
|
||||
* definition is found. */
|
||||
#if defined( configCLINT_BASE_ADDRESS ) && !defined( configMTIME_BASE_ADDRESS ) && ( configCLINT_BASE_ADDRESS == 0 )
|
||||
|
||||
/* Legacy case where configCLINT_BASE_ADDRESS was defined as 0 to indicate
|
||||
* there was no CLINT. Equivalent now is to set the MTIME and MTIMECMP
|
||||
* addresses to 0. */
|
||||
#define configMTIME_BASE_ADDRESS ( 0 )
|
||||
#define configMTIMECMP_BASE_ADDRESS ( 0 )
|
||||
#elif defined( configCLINT_BASE_ADDRESS ) && !defined( configMTIME_BASE_ADDRESS )
|
||||
|
||||
/* Legacy case where configCLINT_BASE_ADDRESS was set to the base address of
|
||||
* the CLINT. Equivalent now is to derive the MTIME and MTIMECMP addresses
|
||||
* from the CLINT address. */
|
||||
#define configMTIME_BASE_ADDRESS ( ( configCLINT_BASE_ADDRESS ) + 0xBFF8UL )
|
||||
#define configMTIMECMP_BASE_ADDRESS ( ( configCLINT_BASE_ADDRESS ) + 0x4000UL )
|
||||
#elif !defined( configMTIME_BASE_ADDRESS ) || !defined( configMTIMECMP_BASE_ADDRESS )
|
||||
#error "configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS must be defined in FreeRTOSConfig.h. Set them to zero if there is no MTIME (machine time) clock. See www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html"
|
||||
#endif /* if defined( configCLINT_BASE_ADDRESS ) && !defined( configMTIME_BASE_ADDRESS ) && ( configCLINT_BASE_ADDRESS == 0 ) */
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
|
||||
#endif /* PORTMACRO_H */
|
||||
+638
@@ -0,0 +1,638 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V11.3.0
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* A sample implementation of pvPortMalloc() and vPortFree() that combines
|
||||
* (coalescences) adjacent memory blocks as they are freed, and in so doing
|
||||
* limits memory fragmentation.
|
||||
*
|
||||
* See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the
|
||||
* memory management pages of https://www.FreeRTOS.org for more information.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
|
||||
* all the API functions to use the MPU wrappers. That should only be done when
|
||||
* task.h is included from an application file. */
|
||||
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||
|
||||
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
|
||||
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
|
||||
#endif
|
||||
|
||||
#ifndef configHEAP_CLEAR_MEMORY_ON_FREE
|
||||
#define configHEAP_CLEAR_MEMORY_ON_FREE 0
|
||||
#endif
|
||||
|
||||
/* Block sizes must not get too small. */
|
||||
#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize << 1 ) )
|
||||
|
||||
/* Assumes 8bit bytes! */
|
||||
#define heapBITS_PER_BYTE ( ( size_t ) 8 )
|
||||
|
||||
/* Max value that fits in a size_t type. */
|
||||
#define heapSIZE_MAX ( ~( ( size_t ) 0 ) )
|
||||
|
||||
/* Check if multiplying a and b will result in overflow. */
|
||||
#define heapMULTIPLY_WILL_OVERFLOW( a, b ) ( ( ( a ) > 0 ) && ( ( b ) > ( heapSIZE_MAX / ( a ) ) ) )
|
||||
|
||||
/* Check if adding a and b will result in overflow. */
|
||||
#define heapADD_WILL_OVERFLOW( a, b ) ( ( a ) > ( heapSIZE_MAX - ( b ) ) )
|
||||
|
||||
/* Check if the subtraction operation ( a - b ) will result in underflow. */
|
||||
#define heapSUBTRACT_WILL_UNDERFLOW( a, b ) ( ( a ) < ( b ) )
|
||||
|
||||
/* MSB of the xBlockSize member of an BlockLink_t structure is used to track
|
||||
* the allocation status of a block. When MSB of the xBlockSize member of
|
||||
* an BlockLink_t structure is set then the block belongs to the application.
|
||||
* When the bit is free the block is still part of the free heap space. */
|
||||
#define heapBLOCK_ALLOCATED_BITMASK ( ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ) )
|
||||
#define heapBLOCK_SIZE_IS_VALID( xBlockSize ) ( ( ( xBlockSize ) & heapBLOCK_ALLOCATED_BITMASK ) == 0 )
|
||||
#define heapBLOCK_IS_ALLOCATED( pxBlock ) ( ( ( pxBlock->xBlockSize ) & heapBLOCK_ALLOCATED_BITMASK ) != 0 )
|
||||
#define heapALLOCATE_BLOCK( pxBlock ) ( ( pxBlock->xBlockSize ) |= heapBLOCK_ALLOCATED_BITMASK )
|
||||
#define heapFREE_BLOCK( pxBlock ) ( ( pxBlock->xBlockSize ) &= ~heapBLOCK_ALLOCATED_BITMASK )
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Allocate the memory for the heap. */
|
||||
#if ( configAPPLICATION_ALLOCATED_HEAP == 1 )
|
||||
|
||||
/* The application writer has already defined the array used for the RTOS
|
||||
* heap - probably so it can be placed in a special segment or address. */
|
||||
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
|
||||
#else
|
||||
PRIVILEGED_DATA static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
|
||||
#endif /* configAPPLICATION_ALLOCATED_HEAP */
|
||||
|
||||
/* Define the linked list structure. This is used to link free blocks in order
|
||||
* of their memory address. */
|
||||
typedef struct A_BLOCK_LINK
|
||||
{
|
||||
struct A_BLOCK_LINK * pxNextFreeBlock; /**< The next free block in the list. */
|
||||
size_t xBlockSize; /**< The size of the free block. */
|
||||
} BlockLink_t;
|
||||
|
||||
/* Setting configENABLE_HEAP_PROTECTOR to 1 enables heap block pointers
|
||||
* protection using an application supplied canary value to catch heap
|
||||
* corruption should a heap buffer overflow occur.
|
||||
*/
|
||||
#if ( configENABLE_HEAP_PROTECTOR == 1 )
|
||||
|
||||
/**
|
||||
* @brief Application provided function to get a random value to be used as canary.
|
||||
*
|
||||
* @param pxHeapCanary [out] Output parameter to return the canary value.
|
||||
*/
|
||||
extern void vApplicationGetRandomHeapCanary( portPOINTER_SIZE_TYPE * pxHeapCanary );
|
||||
|
||||
/* Canary value for protecting internal heap pointers. */
|
||||
PRIVILEGED_DATA static portPOINTER_SIZE_TYPE xHeapCanary;
|
||||
|
||||
/* Macro to load/store BlockLink_t pointers to memory. By XORing the
|
||||
* pointers with a random canary value, heap overflows will result
|
||||
* in randomly unpredictable pointer values which will be caught by
|
||||
* heapVALIDATE_BLOCK_POINTER assert. */
|
||||
#define heapPROTECT_BLOCK_POINTER( pxBlock ) ( ( BlockLink_t * ) ( ( ( portPOINTER_SIZE_TYPE ) ( pxBlock ) ) ^ xHeapCanary ) )
|
||||
#else
|
||||
|
||||
#define heapPROTECT_BLOCK_POINTER( pxBlock ) ( pxBlock )
|
||||
|
||||
#endif /* configENABLE_HEAP_PROTECTOR */
|
||||
|
||||
/* Assert that a heap block pointer is within the heap bounds. */
|
||||
#define heapVALIDATE_BLOCK_POINTER( pxBlock ) \
|
||||
configASSERT( ( ( uint8_t * ) ( pxBlock ) >= &( ucHeap[ 0 ] ) ) && \
|
||||
( ( uint8_t * ) ( pxBlock ) <= &( ucHeap[ configTOTAL_HEAP_SIZE - 1 ] ) ) )
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Inserts a block of memory that is being freed into the correct position in
|
||||
* the list of free memory blocks. The block being freed will be merged with
|
||||
* the block in front it and/or the block behind it if the memory blocks are
|
||||
* adjacent to each other.
|
||||
*/
|
||||
static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/*
|
||||
* Called automatically to setup the required heap structures the first time
|
||||
* pvPortMalloc() is called.
|
||||
*/
|
||||
static void prvHeapInit( void ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* The size of the structure placed at the beginning of each allocated memory
|
||||
* block must by correctly byte aligned. */
|
||||
static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
|
||||
|
||||
/* Create a couple of list links to mark the start and end of the list. */
|
||||
PRIVILEGED_DATA static BlockLink_t xStart;
|
||||
PRIVILEGED_DATA static BlockLink_t * pxEnd = NULL;
|
||||
|
||||
/* Keeps track of the number of calls to allocate and free memory as well as the
|
||||
* number of free bytes remaining, but says nothing about fragmentation. */
|
||||
PRIVILEGED_DATA static size_t xFreeBytesRemaining = ( size_t ) 0U;
|
||||
PRIVILEGED_DATA static size_t xMinimumEverFreeBytesRemaining = ( size_t ) 0U;
|
||||
PRIVILEGED_DATA static size_t xNumberOfSuccessfulAllocations = ( size_t ) 0U;
|
||||
PRIVILEGED_DATA static size_t xNumberOfSuccessfulFrees = ( size_t ) 0U;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void * pvPortMalloc( size_t xWantedSize )
|
||||
{
|
||||
BlockLink_t * pxBlock;
|
||||
BlockLink_t * pxPreviousBlock;
|
||||
BlockLink_t * pxNewBlockLink;
|
||||
void * pvReturn = NULL;
|
||||
size_t xAdditionalRequiredSize;
|
||||
size_t xAllocatedBlockSize = 0;
|
||||
|
||||
if( xWantedSize > 0 )
|
||||
{
|
||||
/* The wanted size must be increased so it can contain a BlockLink_t
|
||||
* structure in addition to the requested amount of bytes. */
|
||||
if( heapADD_WILL_OVERFLOW( xWantedSize, xHeapStructSize ) == 0 )
|
||||
{
|
||||
xWantedSize += xHeapStructSize;
|
||||
|
||||
/* Ensure that blocks are always aligned to the required number
|
||||
* of bytes. */
|
||||
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
|
||||
{
|
||||
/* Byte alignment required. */
|
||||
xAdditionalRequiredSize = portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK );
|
||||
|
||||
if( heapADD_WILL_OVERFLOW( xWantedSize, xAdditionalRequiredSize ) == 0 )
|
||||
{
|
||||
xWantedSize += xAdditionalRequiredSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
xWantedSize = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
xWantedSize = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
vTaskSuspendAll();
|
||||
{
|
||||
/* If this is the first call to malloc then the heap will require
|
||||
* initialisation to setup the list of free blocks. */
|
||||
if( pxEnd == NULL )
|
||||
{
|
||||
prvHeapInit();
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
/* Check the block size we are trying to allocate is not so large that the
|
||||
* top bit is set. The top bit of the block size member of the BlockLink_t
|
||||
* structure is used to determine who owns the block - the application or
|
||||
* the kernel, so it must be free. */
|
||||
if( heapBLOCK_SIZE_IS_VALID( xWantedSize ) != 0 )
|
||||
{
|
||||
if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
|
||||
{
|
||||
/* Traverse the list from the start (lowest address) block until
|
||||
* one of adequate size is found. */
|
||||
pxPreviousBlock = &xStart;
|
||||
pxBlock = heapPROTECT_BLOCK_POINTER( xStart.pxNextFreeBlock );
|
||||
heapVALIDATE_BLOCK_POINTER( pxBlock );
|
||||
|
||||
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != heapPROTECT_BLOCK_POINTER( NULL ) ) )
|
||||
{
|
||||
pxPreviousBlock = pxBlock;
|
||||
pxBlock = heapPROTECT_BLOCK_POINTER( pxBlock->pxNextFreeBlock );
|
||||
heapVALIDATE_BLOCK_POINTER( pxBlock );
|
||||
}
|
||||
|
||||
/* If the end marker was reached then a block of adequate size
|
||||
* was not found. */
|
||||
if( pxBlock != pxEnd )
|
||||
{
|
||||
/* Return the memory space pointed to - jumping over the
|
||||
* BlockLink_t structure at its start. */
|
||||
pvReturn = ( void * ) ( ( ( uint8_t * ) heapPROTECT_BLOCK_POINTER( pxPreviousBlock->pxNextFreeBlock ) ) + xHeapStructSize );
|
||||
heapVALIDATE_BLOCK_POINTER( pvReturn );
|
||||
|
||||
/* This block is being returned for use so must be taken out
|
||||
* of the list of free blocks. */
|
||||
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
|
||||
|
||||
/* If the block is larger than required it can be split into
|
||||
* two. */
|
||||
configASSERT( heapSUBTRACT_WILL_UNDERFLOW( pxBlock->xBlockSize, xWantedSize ) == 0 );
|
||||
|
||||
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
|
||||
{
|
||||
/* This block is to be split into two. Create a new
|
||||
* block following the number of bytes requested. The void
|
||||
* cast is used to prevent byte alignment warnings from the
|
||||
* compiler. */
|
||||
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
|
||||
configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );
|
||||
|
||||
/* Calculate the sizes of two blocks split from the
|
||||
* single block. */
|
||||
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
|
||||
pxBlock->xBlockSize = xWantedSize;
|
||||
|
||||
/* Insert the new block into the list of free blocks. */
|
||||
pxNewBlockLink->pxNextFreeBlock = pxPreviousBlock->pxNextFreeBlock;
|
||||
pxPreviousBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxNewBlockLink );
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
xFreeBytesRemaining -= pxBlock->xBlockSize;
|
||||
|
||||
if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
|
||||
{
|
||||
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
xAllocatedBlockSize = pxBlock->xBlockSize;
|
||||
|
||||
/* The block is being returned - it is allocated and owned
|
||||
* by the application and has no "next" block. */
|
||||
heapALLOCATE_BLOCK( pxBlock );
|
||||
pxBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( NULL );
|
||||
xNumberOfSuccessfulAllocations++;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
traceMALLOC( pvReturn, xAllocatedBlockSize );
|
||||
|
||||
/* Prevent compiler warnings when trace macros are not used. */
|
||||
( void ) xAllocatedBlockSize;
|
||||
}
|
||||
( void ) xTaskResumeAll();
|
||||
|
||||
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
|
||||
{
|
||||
if( pvReturn == NULL )
|
||||
{
|
||||
vApplicationMallocFailedHook();
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
#endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */
|
||||
|
||||
configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
|
||||
return pvReturn;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vPortFree( void * pv )
|
||||
{
|
||||
uint8_t * puc = ( uint8_t * ) pv;
|
||||
BlockLink_t * pxLink;
|
||||
|
||||
if( pv != NULL )
|
||||
{
|
||||
/* The memory being freed will have an BlockLink_t structure immediately
|
||||
* before it. */
|
||||
puc -= xHeapStructSize;
|
||||
|
||||
/* This casting is to keep the compiler from issuing warnings. */
|
||||
pxLink = ( void * ) puc;
|
||||
|
||||
heapVALIDATE_BLOCK_POINTER( pxLink );
|
||||
configASSERT( heapBLOCK_IS_ALLOCATED( pxLink ) != 0 );
|
||||
configASSERT( pxLink->pxNextFreeBlock == heapPROTECT_BLOCK_POINTER( NULL ) );
|
||||
|
||||
if( heapBLOCK_IS_ALLOCATED( pxLink ) != 0 )
|
||||
{
|
||||
if( pxLink->pxNextFreeBlock == heapPROTECT_BLOCK_POINTER( NULL ) )
|
||||
{
|
||||
/* The block is being returned to the heap - it is no longer
|
||||
* allocated. */
|
||||
heapFREE_BLOCK( pxLink );
|
||||
#if ( configHEAP_CLEAR_MEMORY_ON_FREE == 1 )
|
||||
{
|
||||
/* Check for underflow as this can occur if xBlockSize is
|
||||
* overwritten in a heap block. */
|
||||
if( heapSUBTRACT_WILL_UNDERFLOW( pxLink->xBlockSize, xHeapStructSize ) == 0 )
|
||||
{
|
||||
( void ) memset( puc + xHeapStructSize, 0, pxLink->xBlockSize - xHeapStructSize );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
vTaskSuspendAll();
|
||||
{
|
||||
/* Add this block to the list of free blocks. */
|
||||
xFreeBytesRemaining += pxLink->xBlockSize;
|
||||
traceFREE( pv, pxLink->xBlockSize );
|
||||
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
|
||||
xNumberOfSuccessfulFrees++;
|
||||
}
|
||||
( void ) xTaskResumeAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
size_t xPortGetFreeHeapSize( void )
|
||||
{
|
||||
return xFreeBytesRemaining;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
size_t xPortGetMinimumEverFreeHeapSize( void )
|
||||
{
|
||||
return xMinimumEverFreeBytesRemaining;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void xPortResetHeapMinimumEverFreeHeapSize( void )
|
||||
{
|
||||
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vPortInitialiseBlocks( void )
|
||||
{
|
||||
/* This just exists to keep the linker quiet. */
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void * pvPortCalloc( size_t xNum,
|
||||
size_t xSize )
|
||||
{
|
||||
void * pv = NULL;
|
||||
|
||||
if( heapMULTIPLY_WILL_OVERFLOW( xNum, xSize ) == 0 )
|
||||
{
|
||||
pv = pvPortMalloc( xNum * xSize );
|
||||
|
||||
if( pv != NULL )
|
||||
{
|
||||
( void ) memset( pv, 0, xNum * xSize );
|
||||
}
|
||||
}
|
||||
|
||||
return pv;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */
|
||||
{
|
||||
BlockLink_t * pxFirstFreeBlock;
|
||||
portPOINTER_SIZE_TYPE uxStartAddress, uxEndAddress;
|
||||
size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;
|
||||
|
||||
/* Ensure the heap starts on a correctly aligned boundary. */
|
||||
uxStartAddress = ( portPOINTER_SIZE_TYPE ) ucHeap;
|
||||
|
||||
if( ( uxStartAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
|
||||
{
|
||||
uxStartAddress += ( portBYTE_ALIGNMENT - 1 );
|
||||
uxStartAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK );
|
||||
xTotalHeapSize -= ( size_t ) ( uxStartAddress - ( portPOINTER_SIZE_TYPE ) ucHeap );
|
||||
}
|
||||
|
||||
#if ( configENABLE_HEAP_PROTECTOR == 1 )
|
||||
{
|
||||
vApplicationGetRandomHeapCanary( &( xHeapCanary ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
/* xStart is used to hold a pointer to the first item in the list of free
|
||||
* blocks. The void cast is used to prevent compiler warnings. */
|
||||
xStart.pxNextFreeBlock = ( void * ) heapPROTECT_BLOCK_POINTER( uxStartAddress );
|
||||
xStart.xBlockSize = ( size_t ) 0;
|
||||
|
||||
/* pxEnd is used to mark the end of the list of free blocks and is inserted
|
||||
* at the end of the heap space. */
|
||||
uxEndAddress = uxStartAddress + ( portPOINTER_SIZE_TYPE ) xTotalHeapSize;
|
||||
uxEndAddress -= ( portPOINTER_SIZE_TYPE ) xHeapStructSize;
|
||||
uxEndAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK );
|
||||
pxEnd = ( BlockLink_t * ) uxEndAddress;
|
||||
pxEnd->xBlockSize = 0;
|
||||
pxEnd->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( NULL );
|
||||
|
||||
/* To start with there is a single free block that is sized to take up the
|
||||
* entire heap space, minus the space taken by pxEnd. */
|
||||
pxFirstFreeBlock = ( BlockLink_t * ) uxStartAddress;
|
||||
pxFirstFreeBlock->xBlockSize = ( size_t ) ( uxEndAddress - ( portPOINTER_SIZE_TYPE ) pxFirstFreeBlock );
|
||||
pxFirstFreeBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxEnd );
|
||||
|
||||
/* Only one block exists - and it covers the entire usable heap space. */
|
||||
xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
|
||||
xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVILEGED_FUNCTION */
|
||||
{
|
||||
BlockLink_t * pxIterator;
|
||||
uint8_t * puc;
|
||||
|
||||
/* Iterate through the list until a block is found that has a higher address
|
||||
* than the block being inserted. */
|
||||
for( pxIterator = &xStart; heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock ) < pxBlockToInsert; pxIterator = heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock ) )
|
||||
{
|
||||
/* Nothing to do here, just iterate to the right position. */
|
||||
}
|
||||
|
||||
if( pxIterator != &xStart )
|
||||
{
|
||||
heapVALIDATE_BLOCK_POINTER( pxIterator );
|
||||
}
|
||||
|
||||
/* Do the block being inserted, and the block it is being inserted after
|
||||
* make a contiguous block of memory? */
|
||||
puc = ( uint8_t * ) pxIterator;
|
||||
|
||||
if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
|
||||
{
|
||||
pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
|
||||
pxBlockToInsert = pxIterator;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
/* Do the block being inserted, and the block it is being inserted before
|
||||
* make a contiguous block of memory? */
|
||||
puc = ( uint8_t * ) pxBlockToInsert;
|
||||
|
||||
if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock ) )
|
||||
{
|
||||
if( heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock ) != pxEnd )
|
||||
{
|
||||
/* Form one big block from the two blocks. */
|
||||
pxBlockToInsert->xBlockSize += heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock )->xBlockSize;
|
||||
pxBlockToInsert->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock )->pxNextFreeBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
pxBlockToInsert->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxEnd );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
|
||||
}
|
||||
|
||||
/* If the block being inserted plugged a gap, so was merged with the block
|
||||
* before and the block after, then it's pxNextFreeBlock pointer will have
|
||||
* already been set, and should not be set here as that would make it point
|
||||
* to itself. */
|
||||
if( pxIterator != pxBlockToInsert )
|
||||
{
|
||||
pxIterator->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxBlockToInsert );
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vPortGetHeapStats( HeapStats_t * pxHeapStats )
|
||||
{
|
||||
BlockLink_t * pxBlock;
|
||||
size_t xBlocks = 0, xMaxSize = 0, xMinSize = SIZE_MAX;
|
||||
|
||||
vTaskSuspendAll();
|
||||
{
|
||||
pxBlock = heapPROTECT_BLOCK_POINTER( xStart.pxNextFreeBlock );
|
||||
|
||||
/* pxBlock will be NULL if the heap has not been initialised. The heap
|
||||
* is initialised automatically when the first allocation is made. */
|
||||
if( pxBlock != NULL )
|
||||
{
|
||||
while( pxBlock != pxEnd )
|
||||
{
|
||||
/* Increment the number of blocks and record the largest block seen
|
||||
* so far. */
|
||||
xBlocks++;
|
||||
|
||||
if( pxBlock->xBlockSize > xMaxSize )
|
||||
{
|
||||
xMaxSize = pxBlock->xBlockSize;
|
||||
}
|
||||
|
||||
if( pxBlock->xBlockSize < xMinSize )
|
||||
{
|
||||
xMinSize = pxBlock->xBlockSize;
|
||||
}
|
||||
|
||||
/* Move to the next block in the chain until the last block is
|
||||
* reached. */
|
||||
pxBlock = heapPROTECT_BLOCK_POINTER( pxBlock->pxNextFreeBlock );
|
||||
}
|
||||
}
|
||||
}
|
||||
( void ) xTaskResumeAll();
|
||||
|
||||
pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize;
|
||||
pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize;
|
||||
pxHeapStats->xNumberOfFreeBlocks = xBlocks;
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
{
|
||||
pxHeapStats->xAvailableHeapSpaceInBytes = xFreeBytesRemaining;
|
||||
pxHeapStats->xNumberOfSuccessfulAllocations = xNumberOfSuccessfulAllocations;
|
||||
pxHeapStats->xNumberOfSuccessfulFrees = xNumberOfSuccessfulFrees;
|
||||
pxHeapStats->xMinimumEverFreeBytesRemaining = xMinimumEverFreeBytesRemaining;
|
||||
}
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Reset the state in this file. This state is normally initialized at start up.
|
||||
* This function must be called by the application before restarting the
|
||||
* scheduler.
|
||||
*/
|
||||
void vPortHeapResetState( void )
|
||||
{
|
||||
pxEnd = NULL;
|
||||
|
||||
xFreeBytesRemaining = ( size_t ) 0U;
|
||||
xMinimumEverFreeBytesRemaining = ( size_t ) 0U;
|
||||
xNumberOfSuccessfulAllocations = ( size_t ) 0U;
|
||||
xNumberOfSuccessfulFrees = ( size_t ) 0U;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
Vendored
+3364
File diff suppressed because it is too large
Load Diff
Vendored
+8872
File diff suppressed because it is too large
Load Diff
+269
@@ -0,0 +1,269 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_alu #(
|
||||
`include "hazard3_config.vh"
|
||||
,
|
||||
`include "hazard3_width_const.vh"
|
||||
) (
|
||||
input wire [W_ALUOP-1:0] aluop,
|
||||
input wire [6:0] funct7_32b,
|
||||
input wire [2:0] funct3_32b,
|
||||
input wire [W_DATA-1:0] op_a,
|
||||
input wire [W_DATA-1:0] op_b,
|
||||
output reg [W_DATA-1:0] result,
|
||||
output wire cmp
|
||||
);
|
||||
|
||||
`include "hazard3_ops.vh"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Fiddle around with add/sub, comparisons etc (all related).
|
||||
|
||||
wire sub = !(aluop == ALUOP_ADD || (|EXTENSION_ZBA && aluop == ALUOP_SHXADD));
|
||||
|
||||
wire inv_op_b = sub && !(
|
||||
aluop == ALUOP_AND || aluop == ALUOP_OR || aluop == ALUOP_XOR || aluop == ALUOP_RS2
|
||||
);
|
||||
|
||||
wire [W_DATA-1:0] op_a_shifted =
|
||||
|EXTENSION_ZBA && aluop == ALUOP_SHXADD ? (
|
||||
!funct3_32b[2] ? op_a << 1 :
|
||||
!funct3_32b[1] ? op_a << 2 : op_a << 3
|
||||
) : op_a;
|
||||
|
||||
wire [W_DATA-1:0] op_b_inv = op_b ^ {W_DATA{inv_op_b}};
|
||||
|
||||
wire [W_DATA-1:0] sum = op_a_shifted + op_b_inv + {{W_DATA-1{1'b0}}, sub};
|
||||
wire [W_DATA-1:0] op_xor = op_a ^ op_b;
|
||||
|
||||
wire cmp_is_unsigned = aluop == ALUOP_LTU ||
|
||||
|EXTENSION_ZBB && aluop == ALUOP_MAXU ||
|
||||
|EXTENSION_ZBB && aluop == ALUOP_MINU;
|
||||
|
||||
wire lt = op_a[W_DATA-1] == op_b[W_DATA-1] ? sum[W_DATA-1] :
|
||||
cmp_is_unsigned ? op_b[W_DATA-1] : op_a[W_DATA-1] ;
|
||||
|
||||
assign cmp = aluop == ALUOP_SUB ? |op_xor : lt;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Separate units for shift, ctz etc
|
||||
|
||||
wire [W_DATA-1:0] shift_dout;
|
||||
wire shift_right_nleft =
|
||||
aluop == ALUOP_SRL ||
|
||||
aluop == ALUOP_SRA ||
|
||||
(|EXTENSION_ZBB && aluop == ALUOP_ROR ) ||
|
||||
(|EXTENSION_ZBS && aluop == ALUOP_BEXT ) ||
|
||||
(|EXTENSION_XH3BEXTM && aluop == ALUOP_BEXTM);
|
||||
|
||||
wire shift_arith = aluop == ALUOP_SRA;
|
||||
wire shift_rotate = |EXTENSION_ZBB & (aluop == ALUOP_ROR || aluop == ALUOP_ROL);
|
||||
|
||||
hazard3_shift_barrel #(
|
||||
`include "hazard3_config_inst.vh"
|
||||
) shifter (
|
||||
.din (op_a),
|
||||
.shamt (op_b[4:0]),
|
||||
.right_nleft (shift_right_nleft),
|
||||
.rotate (shift_rotate),
|
||||
.arith (shift_arith),
|
||||
.dout (shift_dout)
|
||||
);
|
||||
|
||||
reg [W_DATA-1:0] op_a_rev;
|
||||
always @ (*) begin: rev_op_a
|
||||
integer i;
|
||||
for (i = 0; i < W_DATA; i = i + 1) begin
|
||||
op_a_rev[i] = op_a[W_DATA - 1 - i];
|
||||
end
|
||||
end
|
||||
|
||||
// "leading" means starting at MSB. This is an LSB-first priority encoder, so
|
||||
// "leading" is reversed and "trailing" is not.
|
||||
wire [W_DATA-1:0] ctz_search_mask = aluop == ALUOP_CLZ ? op_a_rev : op_a;
|
||||
wire [W_SHAMT:0] ctz_clz;
|
||||
|
||||
hazard3_priority_encode #(
|
||||
.W_REQ (W_DATA),
|
||||
.HIGHEST_WINS (0)
|
||||
) ctz_priority_encode (
|
||||
.req (ctz_search_mask),
|
||||
.gnt (ctz_clz[W_SHAMT-1:0])
|
||||
);
|
||||
// Special case: all-zeroes returns XLEN
|
||||
assign ctz_clz[W_SHAMT] = ~|op_a;
|
||||
|
||||
reg [W_SHAMT:0] cpop;
|
||||
always @ (*) begin: cpop_count
|
||||
integer i;
|
||||
cpop = {W_SHAMT+1{1'b0}};
|
||||
for (i = 0; i < W_DATA; i = i + 1) begin
|
||||
cpop = cpop + {{W_SHAMT{1'b0}}, op_a[i]};
|
||||
end
|
||||
end
|
||||
|
||||
reg [2*W_DATA-1:0] clmul64;
|
||||
|
||||
always @ (*) begin: clmul_mul
|
||||
integer i;
|
||||
clmul64 = {2*W_DATA{1'b0}};
|
||||
for (i = 0; i < W_DATA; i = i + 1) begin
|
||||
clmul64 = clmul64 ^ (({{W_DATA{1'b0}}, op_a} << i) & {2*W_DATA{op_b[i]}});
|
||||
end
|
||||
end
|
||||
|
||||
// funct3: 1=clmul, 2=clmulr, 3=clmulh, never 0.
|
||||
wire [W_DATA-1:0] clmul =
|
||||
!funct3_32b[1] ? clmul64[31: 0] :
|
||||
!funct3_32b[0] ? clmul64[62:31] : clmul64[63:32];
|
||||
|
||||
reg [W_DATA-1:0] zip;
|
||||
reg [W_DATA-1:0] unzip;
|
||||
always @ (*) begin: do_zip_unzip
|
||||
integer i;
|
||||
for (i = 0; i < W_DATA; i = i + 1) begin
|
||||
zip[i] = op_a[{i[0], i[4:1]}]; // Alternate high/low halves
|
||||
unzip[i] = op_a[{i[3:0], i[4]}]; // All even then all odd
|
||||
end
|
||||
end
|
||||
|
||||
reg [W_DATA-1:0] xperm8;
|
||||
always @ (*) begin: do_xperm8
|
||||
integer i;
|
||||
for (i = 0; i < W_DATA; i = i + 8) begin
|
||||
if (|op_b[i + 2 +: 6]) begin
|
||||
xperm8[i +: 8] = 8'h00;
|
||||
end else begin
|
||||
xperm8[i +: 8] = op_a[8 * op_b[i +: 2] +: 8];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
reg [W_DATA-1:0] xperm4;
|
||||
always @ (*) begin: do_xperm4
|
||||
integer i;
|
||||
for (i = 0; i < W_DATA; i = i + 4) begin
|
||||
if (op_b[i + 3]) begin
|
||||
xperm4[i +: 4] = 4'h0;
|
||||
end else begin
|
||||
xperm4[i +: 4] = op_a[4 * op_b[i +: 3] +: 4];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Output mux, with simple operations inline
|
||||
|
||||
// iCE40: We can implement all bitwise ops with 1 LUT4/bit total, since each
|
||||
// result bit uses only two operand bits. Much better than feeding each into
|
||||
// main mux tree. Doesn't matter for big-LUT FPGAs or for implementations with
|
||||
// bitmanip extensions enabled.
|
||||
|
||||
reg [W_DATA-1:0] bitwise;
|
||||
|
||||
always @ (*) begin: bitwise_ops
|
||||
case (aluop[1:0])
|
||||
ALUOP_AND[1:0]: bitwise = op_a & op_b_inv;
|
||||
ALUOP_OR [1:0]: bitwise = op_a | op_b_inv;
|
||||
ALUOP_XOR[1:0]: bitwise = op_a ^ op_b_inv;
|
||||
ALUOP_RS2[1:0]: bitwise = op_b_inv;
|
||||
endcase
|
||||
end
|
||||
|
||||
wire [W_DATA-1:0] zbs_mask = {{W_DATA-1{1'b0}}, 1'b1} << op_b[W_SHAMT-1:0];
|
||||
|
||||
always @ (*) begin
|
||||
casez ({|EXTENSION_A, |EXTENSION_ZBA, |EXTENSION_ZBB, |EXTENSION_ZBC,
|
||||
|EXTENSION_ZBS, |EXTENSION_ZBKB, |EXTENSION_ZBKX, |EXTENSION_XH3BEXTM, aluop})
|
||||
// Base ISA
|
||||
{8'bzzzzzzzz, ALUOP_ADD }: result = sum;
|
||||
{8'bzzzzzzzz, ALUOP_SUB }: result = sum;
|
||||
{8'bzzzzzzzz, ALUOP_LT }: result = {{W_DATA-1{1'b0}}, lt};
|
||||
{8'bzzzzzzzz, ALUOP_LTU }: result = {{W_DATA-1{1'b0}}, lt};
|
||||
{8'bzzzzzzzz, ALUOP_SRL }: result = shift_dout;
|
||||
{8'bzzzzzzzz, ALUOP_SRA }: result = shift_dout;
|
||||
{8'bzzzzzzzz, ALUOP_SLL }: result = shift_dout;
|
||||
// A or Zbb (written this way to avoid case overlap)
|
||||
{8'b1zzzzzzz, ALUOP_MAX },
|
||||
{8'b0z1zzzzz, ALUOP_MAX }: result = lt ? op_b : op_a;
|
||||
{8'b1zzzzzzz, ALUOP_MIN },
|
||||
{8'b0z1zzzzz, ALUOP_MIN }: result = lt ? op_a : op_b;
|
||||
{8'b1zzzzzzz, ALUOP_MAXU },
|
||||
{8'b0z1zzzzz, ALUOP_MAXU }: result = lt ? op_b : op_a;
|
||||
{8'b1zzzzzzz, ALUOP_MINU },
|
||||
{8'b0z1zzzzz, ALUOP_MINU }: result = lt ? op_a : op_b;
|
||||
// Zba
|
||||
{8'bz1zzzzzz, ALUOP_SHXADD }: result = sum;
|
||||
// Zbb
|
||||
{8'bzz1zzzzz, ALUOP_ANDN }: result = bitwise;
|
||||
{8'bzz1zzzzz, ALUOP_ORN }: result = bitwise;
|
||||
{8'bzz1zzzzz, ALUOP_XNOR }: result = bitwise;
|
||||
{8'bzz1zzzzz, ALUOP_CLZ }: result = {{W_DATA-W_SHAMT-1{1'b0}}, ctz_clz};
|
||||
{8'bzz1zzzzz, ALUOP_CTZ }: result = {{W_DATA-W_SHAMT-1{1'b0}}, ctz_clz};
|
||||
{8'bzz1zzzzz, ALUOP_CPOP }: result = {{W_DATA-W_SHAMT-1{1'b0}}, cpop};
|
||||
{8'bzz1zzzzz, ALUOP_SEXT_B }: result = {{W_DATA-8{op_a[7]}}, op_a[7:0]};
|
||||
{8'bzz1zzzzz, ALUOP_SEXT_H }: result = {{W_DATA-16{op_a[15]}}, op_a[15:0]};
|
||||
{8'bzz1zzzzz, ALUOP_ZEXT_H }: result = {{W_DATA-16{1'b0}}, op_a[15:0]};
|
||||
{8'bzz1zzzzz, ALUOP_ORC_B }: result = {{8{|op_a[31:24]}}, {8{|op_a[23:16]}}, {8{|op_a[15:8]}}, {8{|op_a[7:0]}}};
|
||||
{8'bzz1zzzzz, ALUOP_REV8 }: result = {op_a[7:0], op_a[15:8], op_a[23:16], op_a[31:24]};
|
||||
{8'bzz1zzzzz, ALUOP_ROL }: result = shift_dout;
|
||||
{8'bzz1zzzzz, ALUOP_ROR }: result = shift_dout;
|
||||
// Zbc
|
||||
{8'bzzz1zzzz, ALUOP_CLMUL }: result = clmul;
|
||||
// Zbs
|
||||
{8'bzzzz1zzz, ALUOP_BCLR }: result = op_a & ~zbs_mask;
|
||||
{8'bzzzz1zzz, ALUOP_BSET }: result = op_a | zbs_mask;
|
||||
{8'bzzzz1zzz, ALUOP_BINV }: result = op_a ^ zbs_mask;
|
||||
{8'bzzzz1zzz, ALUOP_BEXT }: result = {{W_DATA-1{1'b0}}, shift_dout[0]};
|
||||
// Zbkb
|
||||
{8'bzzzzz1zz, ALUOP_PACK }: result = {op_b[15:0], op_a[15:0]};
|
||||
{8'bzzzzz1zz, ALUOP_PACKH }: result = {{W_DATA-16{1'b0}}, op_b[7:0], op_a[7:0]};
|
||||
{8'bzzzzz1zz, ALUOP_BREV8 }: result = {op_a_rev[7:0], op_a_rev[15:8], op_a_rev[23:16], op_a_rev[31:24]};
|
||||
{8'bzzzzz1zz, ALUOP_UNZIP }: result = unzip;
|
||||
{8'bzzzzz1zz, ALUOP_ZIP }: result = zip;
|
||||
// Zbkx
|
||||
{8'bzzzzzz1z, ALUOP_XPERM }: result = funct3_32b[2] ? xperm8 : xperm4;
|
||||
// Xh3bextm
|
||||
{8'bzzzzzzz1, ALUOP_BEXTM }: result = shift_dout & {24'h0, {~(8'hfe << funct7_32b[3:1])}};
|
||||
|
||||
default: result = bitwise;
|
||||
endcase
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Properties for base-ISA instructions
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
`ifndef RISCV_FORMAL
|
||||
// Really we're just interested in the shifts and comparisons, as these are
|
||||
// the nontrivial ones. However, easier to test everything!
|
||||
|
||||
wire clk;
|
||||
always @ (posedge clk) begin
|
||||
case(aluop)
|
||||
default: begin end
|
||||
ALUOP_ADD: assert(result == op_a + op_b);
|
||||
ALUOP_SUB: assert(result == op_a - op_b);
|
||||
ALUOP_LT: assert(result == $signed(op_a) < $signed(op_b));
|
||||
ALUOP_LTU: assert(result == op_a < op_b);
|
||||
ALUOP_AND: assert(result == (op_a & op_b));
|
||||
ALUOP_OR: assert(result == (op_a | op_b));
|
||||
ALUOP_XOR: assert(result == (op_a ^ op_b));
|
||||
ALUOP_SRL: assert(result == op_a >> op_b[4:0]);
|
||||
ALUOP_SRA: assert($signed(result) == $signed(op_a) >>> $signed(op_b[4:0]));
|
||||
ALUOP_SLL: assert(result == op_a << op_b[4:0]);
|
||||
endcase
|
||||
end
|
||||
`endif
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
// The branch decision path through the ALU is slow because:
|
||||
//
|
||||
// - Sees immediates and PC on its inputs, as well as regs
|
||||
// - Add/sub rather than just add (with complex decode of the sub condition)
|
||||
// - 2 extra mux layers in front of adder if Zba extension is enabled
|
||||
//
|
||||
// So there is sometimes timing benefit to a dedicated branch comparator.
|
||||
|
||||
module hazard3_branchcmp #(
|
||||
`include "hazard3_config.vh"
|
||||
,
|
||||
`include "hazard3_width_const.vh"
|
||||
) (
|
||||
input wire [31:0] cir,
|
||||
input wire [W_DATA-1:0] op_a,
|
||||
input wire [W_DATA-1:0] op_b,
|
||||
output wire cmp
|
||||
);
|
||||
|
||||
`include "hazard3_ops.vh"
|
||||
|
||||
wire [W_DATA-1:0] diff = op_a - op_b;
|
||||
|
||||
// funct3 instruction
|
||||
// ------------------
|
||||
// 000 BEQ
|
||||
// 001 BNE
|
||||
// 100 BLT
|
||||
// 101 BGE
|
||||
// 110 BLTU
|
||||
// 111 BGEU
|
||||
|
||||
wire cmp_is_unsigned = cir[13];
|
||||
|
||||
wire lt = op_a[W_DATA-1] == op_b[W_DATA-1] ? diff[W_DATA-1] :
|
||||
cmp_is_unsigned ? op_b[W_DATA-1] :
|
||||
op_a[W_DATA-1] ;
|
||||
|
||||
assign cmp = cir[14] ? lt : op_a != op_b;
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// MUL-only (cfg: MUL_FAST) and MUL/MULH/MULHU/MULHSU (cfg: MUL_FAST &&
|
||||
// MULH_FAST) are handled by different circuits. In either case it's a simple
|
||||
// behavioural multiply, and we rely on inference to get good performance on
|
||||
// FPGA.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_mul_fast #(
|
||||
`include "hazard3_config.vh"
|
||||
,
|
||||
`include "hazard3_width_const.vh"
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
|
||||
input wire [W_MULOP-1:0] op,
|
||||
input wire op_vld,
|
||||
input wire [W_DATA-1:0] op_a,
|
||||
input wire [W_DATA-1:0] op_b,
|
||||
|
||||
output wire [W_DATA-1:0] result,
|
||||
output reg result_vld
|
||||
);
|
||||
|
||||
`include "hazard3_ops.vh"
|
||||
|
||||
localparam XLEN = W_DATA;
|
||||
|
||||
//synthesis translate_off
|
||||
generate if (MULH_FAST && !MUL_FAST) begin: err_require_mul_fast_for_mulh
|
||||
initial $fatal("%m: MULH_FAST requires that MUL_FAST is also set.");
|
||||
end endgenerate
|
||||
generate if (MUL_FASTER && !MUL_FAST) begin: err_require_mul_fast_for_faster
|
||||
initial $fatal("%m: MUL_FASTER requires that MUL_FAST is also set.");
|
||||
end endgenerate
|
||||
//synthesis translate_on
|
||||
|
||||
// Latency of 1:
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
result_vld <= 1'b0;
|
||||
end else begin
|
||||
result_vld <= op_vld;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Fast MUL only
|
||||
|
||||
generate
|
||||
if (!MULH_FAST) begin: mul_only
|
||||
|
||||
// This pipestage is folded into the front of the DSP tiles on UP5k. Note the
|
||||
// intention is to register the bypassed core regs at the end of X (since
|
||||
// bypass is quite slow), then perform multiply combinatorially in stage M,
|
||||
// and mux into MW result register.
|
||||
|
||||
reg [XLEN-1:0] op_a_r;
|
||||
reg [XLEN-1:0] op_b_r;
|
||||
|
||||
if (MUL_FASTER) begin: op_passthrough
|
||||
always @ (*) begin
|
||||
op_a_r = op_a;
|
||||
op_b_r = op_b;
|
||||
end
|
||||
end else begin: op_register
|
||||
always @ (posedge clk) begin
|
||||
if (op_vld) begin
|
||||
op_a_r <= op_a;
|
||||
op_b_r <= op_b;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// This should be inferred as 3 DSP tiles on UP5k:
|
||||
//
|
||||
// 1. Register then multiply a[15: 0] and b[15: 0]
|
||||
// 2. Register then multiply a[31:16] and b[15: 0], then directly add output of 1
|
||||
// 3. Register then multiply a[15: 0] and b[31:16], then directly add output of 2
|
||||
//
|
||||
// So there is quite a long path (1x 16-bit multiply, then 2x 16-bit add). On
|
||||
// other platforms you may just end up with a pile of gates.
|
||||
|
||||
`ifndef RISCV_FORMAL_ALTOPS
|
||||
|
||||
assign result = op_a_r * op_b_r;
|
||||
|
||||
`else
|
||||
|
||||
// riscv-formal can use a simpler function, since it's just confirming the
|
||||
// result is correctly hooked up.
|
||||
assign result = result_vld ? (op_a_r + op_b_r) ^ 32'h5876063e : 32'hdeadbeef;
|
||||
|
||||
`endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Fast MUL/MULH/MULHU/MULHSU
|
||||
|
||||
end else begin: mul_and_mulh
|
||||
|
||||
reg [XLEN-1:0] op_a_r;
|
||||
reg [XLEN-1:0] op_b_r;
|
||||
reg [W_MULOP-1:0] op_r;
|
||||
|
||||
if (MUL_FASTER) begin: op_passthrough
|
||||
always @ (*) begin
|
||||
op_a_r = op_a;
|
||||
op_b_r = op_b;
|
||||
op_r = op;
|
||||
end
|
||||
end else begin: op_register
|
||||
always @ (posedge clk) begin
|
||||
if (op_vld) begin
|
||||
op_a_r <= op_a;
|
||||
op_b_r <= op_b;
|
||||
op_r <= op;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
wire op_a_signed = op_r == M_OP_MULH || op_r == M_OP_MULHSU;
|
||||
wire op_b_signed = op_r == M_OP_MULH;
|
||||
|
||||
wire [2*XLEN-1:0] op_a_sext = {
|
||||
{XLEN{op_a_r[XLEN - 1] && op_a_signed}},
|
||||
op_a_r
|
||||
};
|
||||
|
||||
wire [2*XLEN-1:0] op_b_sext = {
|
||||
{XLEN{op_b_r[XLEN - 1] && op_b_signed}},
|
||||
op_b_r
|
||||
};
|
||||
|
||||
wire [2*XLEN-1:0] result_full = op_a_sext * op_b_sext;
|
||||
|
||||
`ifndef RISCV_FORMAL_ALTOPS
|
||||
|
||||
assign result = op_r == M_OP_MUL ? result_full[0 +: XLEN] : result_full[XLEN +: XLEN];
|
||||
|
||||
`else
|
||||
|
||||
assign result =
|
||||
op_r == M_OP_MULH ? (op_a_r + op_b_r) ^ 32'hf6583fb7 :
|
||||
op_r == M_OP_MULHSU ? (op_a_r - op_b_r) ^ 32'hecfbe137 :
|
||||
op_r == M_OP_MULHU ? (op_a_r + op_b_r) ^ 32'h949ce5e8 :
|
||||
op_r == M_OP_MUL ? (op_a_r + op_b_r) ^ 32'h5876063e : 32'hdeadbeef;
|
||||
|
||||
`endif
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Combined multiply/divide/modulo circuit. All operations performed at 1 bit
|
||||
// per clock; aiming for minimal resource usage on iCE40 FPGA. Optionally the
|
||||
// circuit can be unrolled for slightly higher performance.
|
||||
//
|
||||
// When op_kill is high, the current calculation halts immediately. op_vld can
|
||||
// be asserted on the same cycle, and the new calculation begins without
|
||||
// delay, regardless of op_rdy. This may be used by the processor on e.g.
|
||||
// mispredict or trap.
|
||||
//
|
||||
// The actual multiply/divide hardware is unsigned. We handle signedness at
|
||||
// input/output.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_muldiv_seq #(
|
||||
`include "hazard3_config.vh"
|
||||
,
|
||||
`include "hazard3_width_const.vh"
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
input wire [W_MULOP-1:0] op,
|
||||
input wire op_vld,
|
||||
output wire op_rdy,
|
||||
input wire op_kill,
|
||||
input wire [W_DATA-1:0] op_a,
|
||||
input wire [W_DATA-1:0] op_b,
|
||||
|
||||
output wire [W_DATA-1:0] result_h, // mulh* or rem*
|
||||
output wire [W_DATA-1:0] result_l, // mul or div*
|
||||
output wire result_vld
|
||||
);
|
||||
|
||||
`include "hazard3_ops.vh"
|
||||
|
||||
//synthesis translate_off
|
||||
generate if (|(MULDIV_UNROLL & (MULDIV_UNROLL - 1)) || ~|MULDIV_UNROLL) begin: err_pow2
|
||||
initial $fatal("%m: MULDIV_UNROLL must be a positive power of 2");
|
||||
end endgenerate
|
||||
//synthesis translate_on
|
||||
|
||||
localparam XLEN = W_DATA;
|
||||
parameter W_CTR = $clog2(XLEN + 1);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Operation decode, operand sign adjustment
|
||||
|
||||
// On the first cycle, op_a and op_b go straight through to the accumulator
|
||||
// and the divisor/multiplicand register. They are then adjusted in-place
|
||||
// on the next cycle. This allows the same circuits to be reused for sign
|
||||
// adjustment before output (and helps input timing).
|
||||
|
||||
reg [W_MULOP-1:0] op_r;
|
||||
reg [2*XLEN-1:0] accum;
|
||||
reg [XLEN-1:0] op_b_r;
|
||||
reg op_a_neg_r;
|
||||
reg op_b_neg_r;
|
||||
|
||||
wire op_a_signed =
|
||||
op_r == M_OP_MULH ||
|
||||
op_r == M_OP_MULHSU ||
|
||||
op_r == M_OP_DIV ||
|
||||
op_r == M_OP_REM;
|
||||
|
||||
wire op_b_signed =
|
||||
op_r == M_OP_MULH ||
|
||||
op_r == M_OP_DIV ||
|
||||
op_r == M_OP_REM;
|
||||
|
||||
wire op_a_neg = op_a_signed && accum[XLEN-1];
|
||||
wire op_b_neg = op_b_signed && op_b_r[XLEN-1];
|
||||
|
||||
// Non-divide parts of the circuit should be constant-folded if all the MUL
|
||||
// operations are handled by the fast multiplier
|
||||
|
||||
wire is_div = op_r[2] || (MUL_FAST && MULH_FAST);
|
||||
|
||||
// Controls for modifying sign of all/part of accumulator
|
||||
wire accum_neg_l;
|
||||
wire accum_inv_h;
|
||||
wire accum_incr_h;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Arithmetic circuit
|
||||
|
||||
// Combinatorials:
|
||||
reg [2*XLEN-1:0] accum_next;
|
||||
reg [2*XLEN-1:0] addend;
|
||||
reg [2*XLEN-1:0] shift_tmp;
|
||||
reg [2*XLEN-1:0] addsub_tmp;
|
||||
reg neg_l_borrow;
|
||||
|
||||
always @ (*) begin: alu
|
||||
integer i;
|
||||
// Multiply/divide iteration layers
|
||||
accum_next = accum;
|
||||
addend = {2*XLEN{1'b0}};
|
||||
addsub_tmp = {2*XLEN{1'b0}};
|
||||
neg_l_borrow = 1'b0;
|
||||
for (i = 0; i < MULDIV_UNROLL; i = i + 1) begin
|
||||
addend = {is_div && |op_b_r, op_b_r, {XLEN-1{1'b0}}};
|
||||
shift_tmp = is_div ? accum_next : accum_next >> 1;
|
||||
addsub_tmp = shift_tmp + addend;
|
||||
accum_next = (is_div ? !addsub_tmp[2 * XLEN - 1] : accum_next[0]) ?
|
||||
addsub_tmp : shift_tmp;
|
||||
if (is_div)
|
||||
accum_next = {accum_next[2*XLEN-2:0], !addsub_tmp[2 * XLEN - 1]};
|
||||
end
|
||||
// Alternative path for negation of all/part of accumulator
|
||||
if (accum_neg_l)
|
||||
{neg_l_borrow, accum_next[XLEN-1:0]} = {~accum[XLEN-1:0]} + 1'b1;
|
||||
if (accum_incr_h || accum_inv_h)
|
||||
accum_next[XLEN +: XLEN] = (accum[XLEN +: XLEN] ^ {XLEN{accum_inv_h}})
|
||||
+ {{XLEN-1{1'b0}}, accum_incr_h};
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Main state machine
|
||||
|
||||
reg sign_preadj_done;
|
||||
reg [W_CTR-1:0] ctr;
|
||||
reg sign_postadj_done;
|
||||
reg sign_postadj_carry;
|
||||
|
||||
localparam CTR_TOP = XLEN[W_CTR-1:0];
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
ctr <= {W_CTR{1'b0}};
|
||||
sign_preadj_done <= 1'b1;
|
||||
sign_postadj_done <= 1'b1;
|
||||
sign_postadj_carry <= 1'b0;
|
||||
op_r <= {W_MULOP{1'b0}};
|
||||
op_a_neg_r <= 1'b0;
|
||||
op_b_neg_r <= 1'b0;
|
||||
op_b_r <= {XLEN{1'b0}};
|
||||
accum <= {XLEN*2{1'b0}};
|
||||
end else if (op_kill || (op_vld && op_rdy)) begin
|
||||
// Initialise circuit with operands + state
|
||||
ctr <= op_vld ? CTR_TOP : {W_CTR{1'b0}};
|
||||
sign_preadj_done <= !op_vld;
|
||||
sign_postadj_done <= !op_vld;
|
||||
sign_postadj_carry <= 1'b0;
|
||||
op_r <= op;
|
||||
op_b_r <= op_b;
|
||||
accum <= {{XLEN{1'b0}}, op_a};
|
||||
end else if (!sign_preadj_done) begin
|
||||
// Pre-adjust sign if necessary, else perform first iteration immediately
|
||||
op_a_neg_r <= op_a_neg;
|
||||
op_b_neg_r <= op_b_neg;
|
||||
sign_preadj_done <= 1'b1;
|
||||
if (accum_neg_l || (op_b_neg ^ is_div)) begin
|
||||
if (accum_neg_l)
|
||||
accum[0 +: XLEN] <= accum_next[0 +: XLEN];
|
||||
if (op_b_neg ^ is_div)
|
||||
op_b_r <= -op_b_r;
|
||||
end else begin
|
||||
ctr <= ctr - MULDIV_UNROLL[W_CTR-1:0];
|
||||
accum <= accum_next;
|
||||
end
|
||||
end else if (|ctr) begin
|
||||
ctr <= ctr - MULDIV_UNROLL[W_CTR-1:0];
|
||||
accum <= accum_next;
|
||||
end else if (!sign_postadj_done || sign_postadj_carry) begin
|
||||
sign_postadj_done <= 1'b1;
|
||||
if (accum_inv_h || accum_incr_h)
|
||||
accum[XLEN +: XLEN] <= accum_next[XLEN +: XLEN];
|
||||
if (accum_neg_l) begin
|
||||
accum[0 +: XLEN] <= accum_next[0 +: XLEN];
|
||||
if (!is_div) begin
|
||||
sign_postadj_carry <= neg_l_borrow;
|
||||
sign_postadj_done <= !neg_l_borrow;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Sign adjustment control
|
||||
|
||||
// Pre-adjustment: for any a, b we want |a|, |b|. Note that the magnitude of any
|
||||
// 32-bit signed integer is representable by a 32-bit unsigned integer.
|
||||
|
||||
// Post-adjustment for division:
|
||||
// We seek q, r to satisfy a = b * q + r, where a and b are given,
|
||||
// and |r| < |b|. One way to do this is if
|
||||
// sgn(r) = sgn(a)
|
||||
// sgn(q) = sgn(a) ^ sgn(b)
|
||||
// This has additional nice properties like
|
||||
// -(a / b) = (-a) / b = a / (-b)
|
||||
|
||||
// Post-adjustment for multiplication:
|
||||
// We have calculated the 2*XLEN result of |a| * |b|.
|
||||
// Negate the entire accumulator if sgn(a) ^ sgn(b).
|
||||
// This is done in two steps (to share div/mod circuit, and avoid 64-bit carry):
|
||||
// - Negate lower half of accumulator, and invert upper half
|
||||
// - Increment upper half if lower half carried
|
||||
|
||||
wire do_postadj = ~|{ctr, sign_postadj_done};
|
||||
wire op_signs_differ = op_a_neg_r ^ op_b_neg_r;
|
||||
|
||||
assign accum_neg_l =
|
||||
!sign_preadj_done && op_a_neg ||
|
||||
do_postadj && !sign_postadj_carry && op_signs_differ && !(is_div && ~|op_b_r);
|
||||
|
||||
assign {accum_incr_h, accum_inv_h} =
|
||||
do_postadj && is_div && op_a_neg_r ? 2'b11 :
|
||||
do_postadj && !is_div && op_signs_differ && !sign_postadj_carry ? 2'b01 :
|
||||
do_postadj && !is_div && op_signs_differ && sign_postadj_carry ? 2'b10 :
|
||||
2'b00 ;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Outputs
|
||||
|
||||
assign op_rdy = ~|{ctr, accum_neg_l, accum_incr_h, accum_inv_h};
|
||||
assign result_vld = op_rdy;
|
||||
|
||||
`ifndef RISCV_FORMAL_ALTOPS
|
||||
|
||||
assign {result_h, result_l} = accum;
|
||||
|
||||
`else
|
||||
|
||||
// Provide arithmetically simpler alternative operations, to speed up formal checks
|
||||
always assert(XLEN == 32);
|
||||
|
||||
reg [XLEN-1:0] fml_a_saved;
|
||||
reg [XLEN-1:0] fml_b_saved;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
fml_a_saved <= {XLEN{1'b0}};
|
||||
fml_b_saved <= {XLEN{1'b0}};
|
||||
end else if (op_vld && op_rdy) begin
|
||||
fml_a_saved <= op_a;
|
||||
fml_b_saved <= op_b;
|
||||
end
|
||||
end
|
||||
|
||||
assign result_h =
|
||||
op_r == M_OP_MULH ? (fml_a_saved + fml_b_saved) ^ 32'hf6583fb7 :
|
||||
op_r == M_OP_MULHSU ? (fml_a_saved - fml_b_saved) ^ 32'hecfbe137 :
|
||||
op_r == M_OP_MULHU ? (fml_a_saved + fml_b_saved) ^ 32'h949ce5e8 :
|
||||
op_r == M_OP_REM ? (fml_a_saved - fml_b_saved) ^ 32'h8da68fa5 :
|
||||
op_r == M_OP_REMU ? (fml_a_saved - fml_b_saved) ^ 32'h3138d0e1 : 32'hdeadbeef;
|
||||
|
||||
assign result_l =
|
||||
op_r == M_OP_MUL ? (fml_a_saved + fml_b_saved) ^ 32'h5876063e :
|
||||
op_r == M_OP_DIV ? (fml_a_saved - fml_b_saved) ^ 32'h7f8529ec :
|
||||
op_r == M_OP_DIVU ? (fml_a_saved - fml_b_saved) ^ 32'h10e8fd70 : 32'hdeadbeef;
|
||||
|
||||
`endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Interface properties
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
|
||||
always @ (posedge clk) if (rst_n && $past(rst_n)) begin: properties
|
||||
integer i;
|
||||
reg alive;
|
||||
|
||||
if ($past(op_rdy && !op_vld))
|
||||
assert(op_rdy);
|
||||
|
||||
if (result_vld && $past(result_vld) && !$past(op_kill))
|
||||
assert($stable({result_h, result_l}));
|
||||
|
||||
// Kill will halt an in-progress operation, but a new operation may be
|
||||
// asserted simultaneously with kill.
|
||||
if ($past(op_kill))
|
||||
assert(op_rdy == !$past(op_vld));
|
||||
|
||||
// We should be periodically ready (liveness property), unless new operations
|
||||
// are forced in immediately, simultaneous with a kill, in which case there
|
||||
// is no intermediate ready state.
|
||||
alive = op_rdy || (op_kill && op_vld);
|
||||
for (i = 1; i <= XLEN / MULDIV_UNROLL + 3; i = i + 1)
|
||||
alive = alive || $past(op_rdy || (op_kill && op_vld), i);
|
||||
assert(alive);
|
||||
end
|
||||
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,31 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// req: one-hot bitmap
|
||||
// idx: index of the sole set bit in req
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_onehot_encode #(
|
||||
parameter W_REQ = 16,
|
||||
parameter W_GNT = $clog2(W_REQ) // do not modify
|
||||
) (
|
||||
input wire [W_REQ-1:0] req,
|
||||
output reg [W_GNT-1:0] gnt
|
||||
);
|
||||
|
||||
always @ (*) begin: encode
|
||||
reg [W_GNT:0] i;
|
||||
gnt = {W_GNT{1'b0}};
|
||||
for (i = 0; i < W_REQ; i = i + 1) begin
|
||||
gnt = gnt | ({W_GNT{req[i[W_GNT-1:0]]}} & i[W_GNT-1:0]);
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,33 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// req: bitmap
|
||||
// idx: bitmap with all bits clear except the least- (HIGHEST_WINS=0) or
|
||||
// most- (HIGHEST_WINS=1) significant set bit in req.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_onehot_priority #(
|
||||
parameter W_REQ = 16,
|
||||
parameter HIGHEST_WINS = 0
|
||||
) (
|
||||
input wire [W_REQ-1:0] req,
|
||||
output reg [W_REQ-1:0] gnt
|
||||
);
|
||||
|
||||
always @ (*) begin: select
|
||||
integer i;
|
||||
for (i = 0; i < W_REQ; i = i + 1) begin
|
||||
gnt[i] = req[i] && ~|(req & (
|
||||
HIGHEST_WINS ? ~({W_REQ{1'b1}} >> (W_REQ - 1 - i)) : ~({W_REQ{1'b1}} << i)
|
||||
));
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,77 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// req: bitmap of requests
|
||||
// priority: packed array of dynamic priority level of each request
|
||||
// gnt: one-hot bitmap with the highest-priority request.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_onehot_priority_dynamic #(
|
||||
parameter W_REQ = 8,
|
||||
parameter N_PRIORITIES = 2,
|
||||
parameter PRIORITY_HIGHEST_WINS = 1, // If 1, numerically highest level has greatest priority.
|
||||
// Otherwise, numerically lowest wins.
|
||||
parameter TIEBREAK_HIGHEST_WINS = 0, // If 1, highest-numbered request at the highest priority
|
||||
// level wins the tiebreak. Otherwise, lowest-numbered.
|
||||
// Do not modify:
|
||||
parameter W_PRIORITY = $clog2(N_PRIORITIES)
|
||||
) (
|
||||
input wire [W_REQ*W_PRIORITY-1:0] pri,
|
||||
input wire [W_REQ-1:0] req,
|
||||
output wire [W_REQ-1:0] gnt
|
||||
);
|
||||
|
||||
// 1. Stratify requests according to their level
|
||||
reg [W_REQ-1:0] req_stratified [0:N_PRIORITIES-1];
|
||||
reg [N_PRIORITIES-1:0] level_has_req;
|
||||
|
||||
always @ (*) begin: stratify
|
||||
reg signed [31:0] i, j;
|
||||
for (i = 0; i < N_PRIORITIES; i = i + 1) begin
|
||||
for (j = 0; j < W_REQ; j = j + 1) begin
|
||||
req_stratified[i][j] = req[j] &&
|
||||
pri[W_PRIORITY * j +: W_PRIORITY] == i[W_PRIORITY-1:0];
|
||||
end
|
||||
level_has_req[i] = |req_stratified[i];
|
||||
end
|
||||
end
|
||||
|
||||
// 2. Select the highest level with active requests
|
||||
wire [N_PRIORITIES-1:0] active_layer_sel;
|
||||
|
||||
hazard3_onehot_priority #(
|
||||
.W_REQ (N_PRIORITIES),
|
||||
.HIGHEST_WINS (PRIORITY_HIGHEST_WINS)
|
||||
) prisel_layer (
|
||||
.req (level_has_req),
|
||||
.gnt (active_layer_sel)
|
||||
);
|
||||
|
||||
// 3. Mask only those requests at this level
|
||||
reg [W_REQ-1:0] reqs_from_highest_layer;
|
||||
|
||||
always @ (*) begin: mux_reqs_by_layer
|
||||
integer i;
|
||||
reqs_from_highest_layer = {W_REQ{1'b0}};
|
||||
for (i = 0; i < N_PRIORITIES; i = i + 1)
|
||||
reqs_from_highest_layer = reqs_from_highest_layer |
|
||||
(req_stratified[i] & {W_REQ{active_layer_sel[i]}});
|
||||
end
|
||||
|
||||
// 4. Do a standard priority select on those requests as a tie break
|
||||
hazard3_onehot_priority #(
|
||||
.W_REQ (W_REQ),
|
||||
.HIGHEST_WINS (TIEBREAK_HIGHEST_WINS)
|
||||
) prisel_tiebreak (
|
||||
.req (reqs_from_highest_layer),
|
||||
.gnt (gnt)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,41 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// req: bitmap
|
||||
// gnt: index of least set bit (HIGHEST_WINS=0) or most set bit (HIGHEST_WINS=1)
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_priority_encode #(
|
||||
parameter W_REQ = 16,
|
||||
parameter HIGHEST_WINS = 0,
|
||||
parameter W_GNT = $clog2(W_REQ) // do not modify
|
||||
) (
|
||||
input wire [W_REQ-1:0] req,
|
||||
output wire [W_GNT-1:0] gnt
|
||||
);
|
||||
|
||||
wire [W_REQ-1:0] gnt_onehot;
|
||||
|
||||
hazard3_onehot_priority #(
|
||||
.W_REQ (W_REQ),
|
||||
.HIGHEST_WINS (HIGHEST_WINS)
|
||||
) priority_u (
|
||||
.req (req),
|
||||
.gnt (gnt_onehot)
|
||||
);
|
||||
|
||||
hazard3_onehot_encode #(
|
||||
.W_REQ (W_REQ)
|
||||
) encode_u (
|
||||
.req (gnt_onehot),
|
||||
.gnt (gnt)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Implement the three shifts (left logical, right logical, right arithmetic)
|
||||
// using a single log-type barrel shifter. Around 240 LUTs for 32 bits.
|
||||
// (7 layers of 32 2-input muxes, some extra LUTs and LUT inputs used for arith)
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_shift_barrel #(
|
||||
`include "hazard3_config.vh"
|
||||
,
|
||||
`include "hazard3_width_const.vh"
|
||||
) (
|
||||
input wire [W_DATA-1:0] din,
|
||||
input wire [W_SHAMT-1:0] shamt,
|
||||
input wire right_nleft,
|
||||
input wire rotate,
|
||||
input wire arith,
|
||||
output reg [W_DATA-1:0] dout
|
||||
);
|
||||
|
||||
reg [W_DATA-1:0] din_rev;
|
||||
reg [W_DATA-1:0] shift_accum;
|
||||
reg sext; // haha
|
||||
|
||||
always @ (*) begin: shift
|
||||
integer i;
|
||||
|
||||
for (i = 0; i < W_DATA; i = i + 1)
|
||||
din_rev[i] = right_nleft ? din[W_DATA - 1 - i] : din[i];
|
||||
|
||||
sext = arith && din_rev[0];
|
||||
|
||||
shift_accum = din_rev;
|
||||
for (i = 0; i < W_SHAMT; i = i + 1) begin
|
||||
if (shamt[i]) begin
|
||||
shift_accum = (shift_accum << (1 << i)) |
|
||||
({W_DATA{sext}} & ~({W_DATA{1'b1}} << (1 << i))) |
|
||||
({W_DATA{rotate && |EXTENSION_ZBB}} & (shift_accum >> (W_DATA - (1 << i))));
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < W_DATA; i = i + 1)
|
||||
dout[i] = right_nleft ? shift_accum[W_DATA - 1 - i] : shift_accum[i];
|
||||
end
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
always @ (*) begin
|
||||
if (right_nleft && arith && !rotate) begin: asr
|
||||
assert($signed(dout) == $signed(din) >>> $signed(shamt));
|
||||
end else if (right_nleft && !arith && !rotate) begin
|
||||
assert(dout == din >> shamt);
|
||||
end else if (!right_nleft && !arith && !rotate) begin
|
||||
assert(dout == din << shamt);
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Quick reference model for sequential unsigned multiply/divide/modulo
|
||||
|
||||
def div_step(w, accum, divisor):
|
||||
sub_tmp = accum - (divisor << (w - 1))
|
||||
underflow = sub_tmp < 0
|
||||
if not underflow:
|
||||
accum = sub_tmp
|
||||
accum = (accum << 1) | (not underflow)
|
||||
return accum
|
||||
|
||||
def divmod(w, dividend, divisor, debug=True):
|
||||
accum = dividend
|
||||
for i in range(w):
|
||||
accum_prev = accum
|
||||
accum = div_step(w, accum, divisor)
|
||||
if debug:
|
||||
print("Step {:02d}: accum {:0{}x} -> {:0{}x}".format(
|
||||
i, accum_prev, int(w / 2), accum, int(w / 2)))
|
||||
return (accum >> w, accum & ((1 << w) - 1))
|
||||
|
||||
def mul_step(w, accum, multiplicand):
|
||||
add_en = accum & 1
|
||||
accum = accum >> 1
|
||||
if add_en:
|
||||
accum += (multiplicand << (w - 1))
|
||||
return accum
|
||||
|
||||
def mul(w, multiplicand, multiplier, debug=True):
|
||||
accum = multiplier
|
||||
for i in range(w):
|
||||
accum_prev = accum
|
||||
accum = mul_step(w, accum, multiplicand)
|
||||
if debug:
|
||||
print("Step {:02d}: accum {:0{}x} -> {:0{}x}".format(
|
||||
i, accum_prev, int(w / 2), accum, int(w / 2)))
|
||||
return (accum >> w, accum & ((1 << w) - 1))
|
||||
|
||||
def divtest(w=4):
|
||||
for i in range(2 ** w):
|
||||
for j in range(1, 2 ** w):
|
||||
gatemod, gatediv = divmod(w, i, j, debug=False)
|
||||
goldmod, golddiv = (i % j, i // j)
|
||||
print("{:02d} % {:02d} = {:02d} (gold {:02d}); ./. = {:02d} (gold {:02d})"
|
||||
.format(i, j, gatemod, goldmod, gatediv, golddiv))
|
||||
assert(gatemod == goldmod)
|
||||
assert(gatediv == golddiv)
|
||||
|
||||
def multest(w=4):
|
||||
for i in range(2 ** w):
|
||||
for j in range(2 ** w):
|
||||
gateh, gatel = mul(w, i, j, debug=False)
|
||||
gold = i * j
|
||||
goldl, goldh = (gold & ((1 << w) - 1), gold >> w)
|
||||
print("{:02d} * {:02d} = ({:02d} (gold {:02d}), {:02d} (gold {:02d})"
|
||||
.format(i, j, gateh, goldh, gatel, goldl))
|
||||
assert(gatel == goldl)
|
||||
assert(gateh == goldh)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Test division:")
|
||||
divtest()
|
||||
print("Test multiplication:")
|
||||
multest()
|
||||
@@ -0,0 +1,200 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// APB-to-APB asynchronous bridge for connecting DTM to DM, in case DTM is in
|
||||
// a different clock domain (e.g. running directly from crystal to get a
|
||||
// fixed baud reference)
|
||||
//
|
||||
// Note this module depends on the hazard3_sync_1bit module (a flop-chain
|
||||
// synchroniser) which should be reimplemented for your FPGA/process.
|
||||
|
||||
`ifndef HAZARD3_REG_KEEP_ATTRIBUTE
|
||||
`define HAZARD3_REG_KEEP_ATTRIBUTE (* keep = 1'b1 *)
|
||||
`endif
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_apb_async_bridge #(
|
||||
parameter W_ADDR = 8,
|
||||
parameter W_DATA = 32,
|
||||
parameter N_SYNC_STAGES = 2
|
||||
) (
|
||||
// Resets assumed to be synchronised externally
|
||||
input wire clk_src,
|
||||
input wire rst_n_src,
|
||||
|
||||
input wire clk_dst,
|
||||
input wire rst_n_dst,
|
||||
|
||||
// APB port from Transport Module
|
||||
input wire src_psel,
|
||||
input wire src_penable,
|
||||
input wire src_pwrite,
|
||||
input wire [W_ADDR-1:0] src_paddr,
|
||||
input wire [W_DATA-1:0] src_pwdata,
|
||||
output wire [W_DATA-1:0] src_prdata,
|
||||
output wire src_pready,
|
||||
output wire src_pslverr,
|
||||
|
||||
// APB port to Debug Module
|
||||
output wire dst_psel,
|
||||
output wire dst_penable,
|
||||
output wire dst_pwrite,
|
||||
output wire [W_ADDR-1:0] dst_paddr,
|
||||
output wire [W_DATA-1:0] dst_pwdata,
|
||||
input wire [W_DATA-1:0] dst_prdata,
|
||||
input wire dst_pready,
|
||||
input wire dst_pslverr
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Clock-crossing registers
|
||||
|
||||
// We're using a modified req/ack handshake:
|
||||
//
|
||||
// - Initially both req and ack are low
|
||||
// - src asserts req high
|
||||
// - dst responds with ack high and begins transfer
|
||||
// - src deasserts req once it sees ack high
|
||||
// - dst deasserts ack once:
|
||||
// - transfer is complete *and*
|
||||
// - dst sees req deasserted
|
||||
// - Once src sees ack low, a new transfer can begin.
|
||||
//
|
||||
// A NRZI toggle handshake might be more appropriate, but can cause spurious
|
||||
// bus accesses when only one side of the link is reset.
|
||||
|
||||
`HAZARD3_REG_KEEP_ATTRIBUTE reg src_req;
|
||||
wire dst_req;
|
||||
|
||||
`HAZARD3_REG_KEEP_ATTRIBUTE reg dst_ack;
|
||||
wire src_ack;
|
||||
|
||||
// Note the launch registers are not resettable. We maintain setup/hold on
|
||||
// launch-to-capture paths thanks to the req/ack handshake. A stray reset
|
||||
// could violate this.
|
||||
//
|
||||
// The req/ack logic itself can be reset safely because the receiving domain
|
||||
// is protected from metastability by a 2FF synchroniser.
|
||||
|
||||
`HAZARD3_REG_KEEP_ATTRIBUTE reg [W_ADDR + W_DATA + 1 -1:0] src_paddr_pwdata_pwrite; // launch
|
||||
`HAZARD3_REG_KEEP_ATTRIBUTE reg [W_ADDR + W_DATA + 1 -1:0] dst_paddr_pwdata_pwrite; // capture
|
||||
|
||||
`HAZARD3_REG_KEEP_ATTRIBUTE reg [W_DATA + 1 -1:0] dst_prdata_pslverr; // launch
|
||||
`HAZARD3_REG_KEEP_ATTRIBUTE reg [W_DATA + 1 -1:0] src_prdata_pslverr; // capture
|
||||
|
||||
hazard3_sync_1bit #(
|
||||
.N_STAGES (N_SYNC_STAGES)
|
||||
) sync_req (
|
||||
.clk (clk_dst),
|
||||
.rst_n (rst_n_dst),
|
||||
.i (src_req),
|
||||
.o (dst_req)
|
||||
);
|
||||
|
||||
hazard3_sync_1bit #(
|
||||
.N_STAGES (N_SYNC_STAGES)
|
||||
) sync_ack (
|
||||
.clk (clk_src),
|
||||
.rst_n (rst_n_src),
|
||||
.i (dst_ack),
|
||||
.o (src_ack)
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// src state machine
|
||||
|
||||
reg src_waiting_for_downstream;
|
||||
reg src_pready_r;
|
||||
|
||||
always @ (posedge clk_src or negedge rst_n_src) begin
|
||||
if (!rst_n_src) begin
|
||||
src_req <= 1'b0;
|
||||
src_waiting_for_downstream <= 1'b0;
|
||||
src_prdata_pslverr <= {W_DATA + 1{1'b0}};
|
||||
src_pready_r <= 1'b1;
|
||||
end else if (src_waiting_for_downstream) begin
|
||||
if (src_req && src_ack) begin
|
||||
// Request was acknowledged, so deassert.
|
||||
src_req <= 1'b0;
|
||||
end else if (!(src_req || src_ack)) begin
|
||||
// Downstream transfer has finished, data is valid.
|
||||
src_pready_r <= 1'b1;
|
||||
src_waiting_for_downstream <= 1'b0;
|
||||
// Note this assignment is cross-domain (but data has been stable
|
||||
// for duration of ack synchronisation delay):
|
||||
src_prdata_pslverr <= dst_prdata_pslverr;
|
||||
end
|
||||
end else begin
|
||||
// paddr, pwdata and pwrite are all valid during the setup phase, and
|
||||
// APB defines the setup phase to always last one cycle and proceed
|
||||
// to access phase. So, we can ignore penable, and pready is ignored.
|
||||
if (src_psel) begin
|
||||
src_pready_r <= 1'b0;
|
||||
src_req <= 1'b1;
|
||||
src_waiting_for_downstream <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Bus request launch register is not resettable
|
||||
always @ (posedge clk_src) begin
|
||||
if (src_psel && !src_waiting_for_downstream)
|
||||
src_paddr_pwdata_pwrite <= {src_paddr, src_pwdata, src_pwrite};
|
||||
end
|
||||
|
||||
assign {src_prdata, src_pslverr} = src_prdata_pslverr;
|
||||
assign src_pready = src_pready_r;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// dst state machine
|
||||
|
||||
wire dst_bus_finish = dst_penable && dst_pready;
|
||||
reg dst_psel_r;
|
||||
reg dst_penable_r;
|
||||
|
||||
always @ (posedge clk_dst or negedge rst_n_dst) begin
|
||||
if (!rst_n_dst) begin
|
||||
dst_ack <= 1'b0;
|
||||
end else if (dst_req) begin
|
||||
dst_ack <= 1'b1;
|
||||
end else if (!dst_req && dst_ack && !dst_psel_r) begin
|
||||
dst_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
always @ (posedge clk_dst or negedge rst_n_dst) begin
|
||||
if (!rst_n_dst) begin
|
||||
dst_psel_r <= 1'b0;
|
||||
dst_penable_r <= 1'b0;
|
||||
dst_paddr_pwdata_pwrite <= {W_ADDR + W_DATA + 1{1'b0}};
|
||||
end else if (dst_req && !dst_ack) begin
|
||||
dst_psel_r <= 1'b1;
|
||||
// Note this assignment is cross-domain. The src register has been
|
||||
// stable for the duration of the req sync delay.
|
||||
dst_paddr_pwdata_pwrite <= src_paddr_pwdata_pwrite;
|
||||
end else if (dst_psel_r && !dst_penable_r) begin
|
||||
dst_penable_r <= 1'b1;
|
||||
end else if (dst_bus_finish) begin
|
||||
dst_psel_r <= 1'b0;
|
||||
dst_penable_r <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// Bus response launch register is not resettable
|
||||
always @ (posedge clk_dst) begin
|
||||
if (dst_bus_finish)
|
||||
dst_prdata_pslverr <= {dst_prdata, dst_pslverr};
|
||||
end
|
||||
|
||||
assign dst_psel = dst_psel_r;
|
||||
assign dst_penable = dst_penable_r;
|
||||
assign {dst_paddr, dst_pwdata, dst_pwrite} = dst_paddr_pwdata_pwrite;
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,41 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// The output is asserted asynchronously when the input is asserted,
|
||||
// but deasserted synchronously when clocked with the input deasserted.
|
||||
// Input and output are both active-low.
|
||||
//
|
||||
// This is a baseline implementation -- you should replace it with cells
|
||||
// specific to your FPGA/process
|
||||
|
||||
`ifndef HAZARD3_REG_KEEP_ATTRIBUTE
|
||||
`define HAZARD3_REG_KEEP_ATTRIBUTE (* keep = 1'b1 *)
|
||||
`endif
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_reset_sync #(
|
||||
parameter N_STAGES = 2 // Should be >= 2
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n_in,
|
||||
output wire rst_n_out
|
||||
);
|
||||
|
||||
`HAZARD3_REG_KEEP_ATTRIBUTE reg [N_STAGES-1:0] delay;
|
||||
|
||||
always @ (posedge clk or negedge rst_n_in)
|
||||
if (!rst_n_in)
|
||||
delay <= {N_STAGES{1'b0}};
|
||||
else
|
||||
delay <= {delay[N_STAGES-2:0], 1'b1};
|
||||
|
||||
assign rst_n_out = delay[N_STAGES-1];
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,39 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// A 2FF synchronizer to mitigate metastabilities. This is a baseline
|
||||
// implementation -- you should replace it with cells specific to your
|
||||
// FPGA/process
|
||||
|
||||
`ifndef HAZARD3_REG_KEEP_ATTRIBUTE
|
||||
`define HAZARD3_REG_KEEP_ATTRIBUTE (* keep = 1'b1 *) (* async_reg *)
|
||||
`endif
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_sync_1bit #(
|
||||
parameter N_STAGES = 2 // Should be >=2
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
input wire i,
|
||||
output wire o
|
||||
);
|
||||
|
||||
`HAZARD3_REG_KEEP_ATTRIBUTE reg [N_STAGES-1:0] sync_flops;
|
||||
|
||||
always @ (posedge clk or negedge rst_n)
|
||||
if (!rst_n)
|
||||
sync_flops <= {N_STAGES{1'b0}};
|
||||
else
|
||||
sync_flops <= {sync_flops[N_STAGES-2:0], i};
|
||||
|
||||
assign o = sync_flops[N_STAGES-1];
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+1
@@ -0,0 +1 @@
|
||||
file hazard3_dm.v
|
||||
+904
@@ -0,0 +1,904 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// RISC-V Debug Module for Hazard3. Supports up to 32 cores (1 hart per core).
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_dm #(
|
||||
// Where there are multiple harts per DM, the least-indexed hart is the
|
||||
// least-significant on each concatenated hart access bus.
|
||||
parameter N_HARTS = 1,
|
||||
// Where there are multiple DMs, the address of each DM should be a
|
||||
// multiple of 'h200, so that bits[8:2] decode correctly.
|
||||
parameter NEXT_DM_ADDR = 32'h0000_0000,
|
||||
// Implement support for system bus access:
|
||||
parameter HAVE_SBA = 0,
|
||||
|
||||
// Do not modify:
|
||||
parameter XLEN = 32, // Do not modify
|
||||
parameter W_HARTSEL = N_HARTS > 1 ? $clog2(N_HARTS) : 1 // Do not modify
|
||||
) (
|
||||
// DM is assumed to be in same clock domain as core; clock crossing
|
||||
// (if any) is inside DTM, or between DTM and DM.
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
|
||||
// APB access from Debug Transport Module
|
||||
input wire dmi_psel,
|
||||
input wire dmi_penable,
|
||||
input wire dmi_pwrite,
|
||||
input wire [8:0] dmi_paddr,
|
||||
input wire [31:0] dmi_pwdata,
|
||||
output reg [31:0] dmi_prdata,
|
||||
output wire dmi_pready,
|
||||
output wire dmi_pslverr,
|
||||
|
||||
// Reset request/acknowledge. "req" is a pulse >= 1 cycle wide. "done" is
|
||||
// level-sensitive, goes high once component is out of reset.
|
||||
//
|
||||
// The "sys" reset (ndmreset) is conventionally everything apart from DM +
|
||||
// DTM, but, as per section 3.2 in 0.13.2 debug spec: "Exactly what is
|
||||
// affected by this reset is implementation dependent, as long as it is
|
||||
// possible to debug programs from the first instruction executed." So
|
||||
// this could simply be an all-hart reset.
|
||||
output wire sys_reset_req,
|
||||
input wire sys_reset_done,
|
||||
output wire [N_HARTS-1:0] hart_reset_req,
|
||||
input wire [N_HARTS-1:0] hart_reset_done,
|
||||
|
||||
// Hart run/halt control
|
||||
output wire [N_HARTS-1:0] hart_req_halt,
|
||||
output wire [N_HARTS-1:0] hart_req_halt_on_reset,
|
||||
output wire [N_HARTS-1:0] hart_req_resume,
|
||||
input wire [N_HARTS-1:0] hart_halted,
|
||||
input wire [N_HARTS-1:0] hart_running,
|
||||
|
||||
// Hart access to data0 CSR (assumed to be core-internal but per-hart)
|
||||
output wire [N_HARTS*XLEN-1:0] hart_data0_rdata,
|
||||
input wire [N_HARTS*XLEN-1:0] hart_data0_wdata,
|
||||
input wire [N_HARTS-1:0] hart_data0_wen,
|
||||
|
||||
// Hart instruction injection
|
||||
output wire [N_HARTS*32-1:0] hart_instr_data,
|
||||
output reg [N_HARTS-1:0] hart_instr_data_vld,
|
||||
input wire [N_HARTS-1:0] hart_instr_data_rdy,
|
||||
input wire [N_HARTS-1:0] hart_instr_caught_exception,
|
||||
input wire [N_HARTS-1:0] hart_instr_caught_ebreak,
|
||||
|
||||
// System bus access (optional) -- can be hooked up to the standalone AHB
|
||||
// shim (hazard3_sbus_to_ahb.v) or the SBA input port on the processor
|
||||
// wrapper, which muxes SBA into the processor's load/store bus access
|
||||
// port. SBA does not increase debugger bus throughput, but supports
|
||||
// minimally intrusive debug bus access for e.g. Segger RTT.
|
||||
output wire [31:0] sbus_addr,
|
||||
output wire sbus_write,
|
||||
output wire [1:0] sbus_size,
|
||||
output wire sbus_vld,
|
||||
input wire sbus_rdy,
|
||||
input wire sbus_err,
|
||||
output wire [31:0] sbus_wdata,
|
||||
input wire [31:0] sbus_rdata
|
||||
);
|
||||
|
||||
wire dmi_write = dmi_psel && dmi_penable && dmi_pready && dmi_pwrite;
|
||||
wire dmi_read = dmi_psel && dmi_penable && dmi_pready && !dmi_pwrite;
|
||||
assign dmi_pready = 1'b1;
|
||||
assign dmi_pslverr = 1'b0;
|
||||
|
||||
// Program buffer is fixed at 2 words plus impebreak. The main thing we care
|
||||
// about is support for efficient memory block transfers using abstractauto;
|
||||
// in this case 2 words + impebreak is sufficient for RV32I, and 1 word +
|
||||
// impebreak is sufficient for RV32IC.
|
||||
localparam PROGBUF_SIZE = 2;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Address constants
|
||||
|
||||
localparam ADDR_DATA0 = 7'h04;
|
||||
// Other data registers not present.
|
||||
localparam ADDR_DMCONTROL = 7'h10;
|
||||
localparam ADDR_DMSTATUS = 7'h11;
|
||||
localparam ADDR_HARTINFO = 7'h12;
|
||||
localparam ADDR_HALTSUM1 = 7'h13;
|
||||
localparam ADDR_HALTSUM0 = 7'h40;
|
||||
// No HALTSUM2+ registers (we don't support >32 harts anyway)
|
||||
localparam ADDR_HAWINDOWSEL = 7'h14;
|
||||
localparam ADDR_HAWINDOW = 7'h15;
|
||||
localparam ADDR_ABSTRACTCS = 7'h16;
|
||||
localparam ADDR_COMMAND = 7'h17;
|
||||
localparam ADDR_ABSTRACTAUTO = 7'h18;
|
||||
localparam ADDR_CONFSTRPTR0 = 7'h19;
|
||||
localparam ADDR_CONFSTRPTR1 = 7'h1a;
|
||||
localparam ADDR_CONFSTRPTR2 = 7'h1b;
|
||||
localparam ADDR_CONFSTRPTR3 = 7'h1c;
|
||||
localparam ADDR_NEXTDM = 7'h1d;
|
||||
localparam ADDR_PROGBUF0 = 7'h20;
|
||||
localparam ADDR_PROGBUF1 = 7'h21;
|
||||
// No authentication
|
||||
localparam ADDR_SBCS = 7'h38;
|
||||
localparam ADDR_SBADDRESS0 = 7'h39;
|
||||
localparam ADDR_SBDATA0 = 7'h3c;
|
||||
|
||||
// APB is byte-addressed, DM registers are word-addressed.
|
||||
wire [6:0] dmi_regaddr = dmi_paddr[8:2];
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Hart selection
|
||||
|
||||
reg dmactive;
|
||||
|
||||
// Some fiddliness to make sure we get a single-wide zero-valued signal when
|
||||
// N_HARTS == 1 (so we can use this for indexing of per-hart signals)
|
||||
reg [W_HARTSEL-1:0] hartsel;
|
||||
wire [W_HARTSEL-1:0] hartsel_next;
|
||||
|
||||
generate
|
||||
if (N_HARTS > 1) begin: has_hartsel
|
||||
|
||||
// Only the lower 10 bits of hartsel are supported
|
||||
assign hartsel_next = dmi_write && dmi_regaddr == ADDR_DMCONTROL ?
|
||||
dmi_pwdata[16 +: W_HARTSEL] : hartsel;
|
||||
|
||||
end else begin: has_no_hartsel
|
||||
|
||||
assign hartsel_next = 1'b0;
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
hartsel <= {W_HARTSEL{1'b0}};
|
||||
end else if (!dmactive) begin
|
||||
hartsel <= {W_HARTSEL{1'b0}};
|
||||
end else begin
|
||||
hartsel <= hartsel_next;
|
||||
end
|
||||
end
|
||||
|
||||
// Also implement the hart array mask if there is more than one hart.
|
||||
reg [N_HARTS-1:0] hart_array_mask;
|
||||
reg hasel;
|
||||
wire [N_HARTS-1:0] hart_array_mask_next;
|
||||
wire hasel_next;
|
||||
|
||||
generate
|
||||
if (N_HARTS > 1) begin: has_array_mask
|
||||
|
||||
assign hart_array_mask_next = dmi_write && dmi_regaddr == ADDR_HAWINDOW ?
|
||||
dmi_pwdata[N_HARTS-1:0] : hart_array_mask;
|
||||
assign hasel_next = dmi_write && dmi_regaddr == ADDR_DMCONTROL ?
|
||||
dmi_pwdata[26] : hasel;
|
||||
|
||||
end else begin: has_no_array_mask
|
||||
|
||||
assign hart_array_mask_next = 1'b0;
|
||||
assign hasel_next = 1'b0;
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
hart_array_mask <= {N_HARTS{1'b0}};
|
||||
hasel <= 1'b0;
|
||||
end else if (!dmactive) begin
|
||||
hart_array_mask <= {N_HARTS{1'b0}};
|
||||
hasel <= 1'b0;
|
||||
end else begin
|
||||
hart_array_mask <= hart_array_mask_next;
|
||||
hasel <= hasel_next;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Run/halt/reset control
|
||||
|
||||
// Normal read/write fields for dmcontrol (note some of these are per-hart
|
||||
// fields that get rotated into dmcontrol based on the current/next hartsel).
|
||||
reg [N_HARTS-1:0] dmcontrol_haltreq;
|
||||
reg [N_HARTS-1:0] dmcontrol_hartreset;
|
||||
reg [N_HARTS-1:0] dmcontrol_resethaltreq;
|
||||
reg dmcontrol_ndmreset;
|
||||
|
||||
wire [N_HARTS-1:0] dmcontrol_op_mask;
|
||||
|
||||
generate
|
||||
if (N_HARTS > 1) begin: dmcontrol_multiple_harts
|
||||
|
||||
// Selection is the hart selected by hartsel, *plus* the hart array mask
|
||||
// if hasel is set. Note we don't need to use the "next" version of
|
||||
// hart_array_mask since it can't change simultaneously with dmcontrol.
|
||||
assign dmcontrol_op_mask =
|
||||
(hartsel_next >= N_HARTS ? {N_HARTS{1'b0}} : {{N_HARTS-1{1'b0}}, 1'b1} << hartsel_next)
|
||||
| ({N_HARTS{hasel_next}} & hart_array_mask);
|
||||
|
||||
end else begin: dmcontrol_single_hart
|
||||
|
||||
assign dmcontrol_op_mask = 1'b1;
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
dmactive <= 1'b0;
|
||||
dmcontrol_ndmreset <= 1'b0;
|
||||
dmcontrol_haltreq <= {N_HARTS{1'b0}};
|
||||
dmcontrol_hartreset <= {N_HARTS{1'b0}};
|
||||
dmcontrol_resethaltreq <= {N_HARTS{1'b0}};
|
||||
end else if (!dmactive) begin
|
||||
// Only dmactive is writable when !dmactive
|
||||
if (dmi_write && dmi_regaddr == ADDR_DMCONTROL)
|
||||
dmactive <= dmi_pwdata[0];
|
||||
dmcontrol_ndmreset <= 1'b0;
|
||||
dmcontrol_haltreq <= {N_HARTS{1'b0}};
|
||||
dmcontrol_hartreset <= {N_HARTS{1'b0}};
|
||||
dmcontrol_resethaltreq <= {N_HARTS{1'b0}};
|
||||
end else if (dmi_write && dmi_regaddr == ADDR_DMCONTROL) begin
|
||||
dmactive <= dmi_pwdata[0];
|
||||
dmcontrol_ndmreset <= dmi_pwdata[1];
|
||||
|
||||
dmcontrol_haltreq <= (dmcontrol_haltreq & ~dmcontrol_op_mask) |
|
||||
({N_HARTS{dmi_pwdata[31]}} & dmcontrol_op_mask);
|
||||
|
||||
dmcontrol_hartreset <= (dmcontrol_hartreset & ~dmcontrol_op_mask) |
|
||||
({N_HARTS{dmi_pwdata[29]}} & dmcontrol_op_mask);
|
||||
|
||||
dmcontrol_resethaltreq <= (dmcontrol_resethaltreq
|
||||
& ~({N_HARTS{dmi_pwdata[2]}} & dmcontrol_op_mask))
|
||||
| ({N_HARTS{dmi_pwdata[3]}} & dmcontrol_op_mask);
|
||||
end
|
||||
end
|
||||
|
||||
assign sys_reset_req = dmcontrol_ndmreset;
|
||||
assign hart_reset_req = dmcontrol_hartreset;
|
||||
assign hart_req_halt = dmcontrol_haltreq;
|
||||
assign hart_req_halt_on_reset = dmcontrol_resethaltreq;
|
||||
|
||||
reg [N_HARTS-1:0] hart_reset_done_prev;
|
||||
reg [N_HARTS-1:0] dmstatus_havereset;
|
||||
wire [N_HARTS-1:0] hart_available = hart_reset_done & {N_HARTS{sys_reset_done}};
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
hart_reset_done_prev <= {N_HARTS{1'b0}};
|
||||
end else begin
|
||||
hart_reset_done_prev <= hart_reset_done;
|
||||
end
|
||||
end
|
||||
|
||||
wire dmcontrol_ackhavereset = dmi_write && dmi_regaddr == ADDR_DMCONTROL && dmi_pwdata[28];
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
dmstatus_havereset <= {N_HARTS{1'b0}};
|
||||
end else if (!dmactive) begin
|
||||
dmstatus_havereset <= {N_HARTS{1'b0}};
|
||||
end else begin
|
||||
dmstatus_havereset <= (dmstatus_havereset | (hart_reset_done & ~hart_reset_done_prev))
|
||||
& ~({N_HARTS{dmcontrol_ackhavereset}} & dmcontrol_op_mask);
|
||||
end
|
||||
end
|
||||
|
||||
reg [N_HARTS-1:0] dmstatus_resumeack;
|
||||
reg [N_HARTS-1:0] dmcontrol_resumereq_sticky;
|
||||
|
||||
// Note: we are required to ignore resumereq when haltreq is also set, as per
|
||||
// spec (odd since the host is forbidden from writing both at once anyway).
|
||||
// The wording is odd, it refers only to `haltreq` which is specifically the
|
||||
// write-only `dmcontrol` field, not the underlying halt request state bits.
|
||||
wire dmcontrol_resumereq = dmi_write && dmi_regaddr == ADDR_DMCONTROL &&
|
||||
dmi_pwdata[30] && !dmi_pwdata[31];
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
dmstatus_resumeack <= {N_HARTS{1'b0}};
|
||||
dmcontrol_resumereq_sticky <= {N_HARTS{1'b0}};
|
||||
end else if (!dmactive) begin
|
||||
dmstatus_resumeack <= {N_HARTS{1'b0}};
|
||||
dmcontrol_resumereq_sticky <= {N_HARTS{1'b0}};
|
||||
end else begin
|
||||
dmstatus_resumeack <= (dmstatus_resumeack
|
||||
| (dmcontrol_resumereq_sticky & hart_running & hart_available))
|
||||
& ~({N_HARTS{dmcontrol_resumereq}} & dmcontrol_op_mask);
|
||||
|
||||
dmcontrol_resumereq_sticky <= (dmcontrol_resumereq_sticky
|
||||
& ~(hart_running & hart_available))
|
||||
| ({N_HARTS{dmcontrol_resumereq}} & dmcontrol_op_mask);
|
||||
end
|
||||
end
|
||||
|
||||
assign hart_req_resume = dmcontrol_resumereq_sticky;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// System bus access
|
||||
|
||||
reg [31:0] sbaddress;
|
||||
reg [31:0] sbdata;
|
||||
|
||||
// Update logic for address/data registers:
|
||||
|
||||
reg sbbusy;
|
||||
reg sbautoincrement;
|
||||
reg [2:0] sbaccess; // Size of the transfer
|
||||
|
||||
wire sbdata_write_blocked;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
sbaddress <= {32{1'b0}};
|
||||
sbdata <= {32{1'b0}};
|
||||
end else if (!dmactive) begin
|
||||
sbaddress <= {32{1'b0}};
|
||||
sbdata <= {32{1'b0}};
|
||||
end else if (HAVE_SBA) begin
|
||||
if (dmi_write && dmi_regaddr == ADDR_SBDATA0 && !sbdata_write_blocked) begin
|
||||
// Note sbbusyerror and sberror block writes to sbdata0, as the
|
||||
// write is required to have no side effects when they are set.
|
||||
sbdata <= dmi_pwdata;
|
||||
end else if (sbus_vld && sbus_rdy && !sbus_write && !sbus_err) begin
|
||||
// Make sure the lower byte lanes see appropriately shifted data as
|
||||
// long as the transfer is naturally aligned
|
||||
sbdata <= sbaddress[1:0] == 2'b01 ? {sbus_rdata[31:8], sbus_rdata[15:8]} :
|
||||
sbaddress[1:0] == 2'b10 ? {sbus_rdata[31:16], sbus_rdata[31:16]} :
|
||||
sbaddress[1:0] == 2'b11 ? {sbus_rdata[31:8], sbus_rdata[31:24]} : sbus_rdata;
|
||||
end
|
||||
if (dmi_write && dmi_regaddr == ADDR_SBADDRESS0 && !sbbusy) begin
|
||||
// Note sbaddress can't be written when busy, but
|
||||
// sberror/sbbusyerror do not prevent writes.
|
||||
sbaddress <= dmi_pwdata;
|
||||
end else if (sbus_vld && sbus_rdy && !sbus_err && sbautoincrement) begin
|
||||
// Note: address increments only following a successful transfer.
|
||||
// Spec 0.13.2 weirdly implies address should increment following
|
||||
// a sbdata0 read with sbautoincrement=1 and sbreadondata=0, but
|
||||
// this seems to be a typo, fixed in later versions.
|
||||
sbaddress <= sbaddress + (
|
||||
sbaccess[1:0] == 2'b00 ? 32'd1 :
|
||||
sbaccess[1:0] == 2'b01 ? 32'd2 : 32'd4
|
||||
);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Control logic:
|
||||
|
||||
reg sbbusyerror;
|
||||
reg sbreadonaddr;
|
||||
reg sbreadondata;
|
||||
reg [2:0] sberror;
|
||||
reg sb_current_is_write;
|
||||
|
||||
localparam SBERROR_OK = 3'h0;
|
||||
localparam SBERROR_BADADDR = 3'h2;
|
||||
localparam SBERROR_BADALIGN = 3'h3;
|
||||
localparam SBERROR_BADSIZE = 3'h4;
|
||||
|
||||
assign sbdata_write_blocked = sbbusy || sbbusyerror || |sberror;
|
||||
|
||||
// Notes on behaviour of sbbusyerror: the sbbusyerror description says:
|
||||
//
|
||||
// "Set when the debugger attempts to read data while a read is in progress,
|
||||
// or when the debugger initiates a new access while one is already in
|
||||
// progress (while sbbusy is set)."
|
||||
//
|
||||
// However, sbaddress0 description says:
|
||||
//
|
||||
// "When the system bus master is busy, writes to this register will set
|
||||
// sbbusyerror and don’t do anything else."
|
||||
//
|
||||
// ...not conditioned on sbreadonaddr. Likewise the sbdata0 description says:
|
||||
//
|
||||
// "If the bus master is busy then accesses set sbbusyerror, and don’t do
|
||||
// anything else."
|
||||
//
|
||||
// ...not conditioned on sbreadondata. We are going to take the union of all
|
||||
// the cases where the spec says we should raise an error:
|
||||
|
||||
wire sb_access_illegal_when_busy =
|
||||
dmi_regaddr == ADDR_SBDATA0 && (dmi_read || dmi_write) ||
|
||||
dmi_regaddr == ADDR_SBADDRESS0 && dmi_write;
|
||||
|
||||
wire sb_want_start_write = dmi_write && dmi_regaddr == ADDR_SBDATA0;
|
||||
|
||||
wire sb_want_start_read =
|
||||
(sbreadonaddr && dmi_write && dmi_regaddr == ADDR_SBADDRESS0) ||
|
||||
(sbreadondata && dmi_read && dmi_regaddr == ADDR_SBDATA0);
|
||||
|
||||
wire [1:0] sb_next_align = sbreadonaddr && dmi_write && dmi_regaddr == ADDR_SBADDRESS0 ?
|
||||
dmi_pwdata[1:0] : sbaddress[1:0];
|
||||
|
||||
wire sb_badalign =
|
||||
(sbaccess == 3'h1 && sb_next_align[0]) ||
|
||||
(sbaccess == 3'h2 && |sb_next_align[1:0]);
|
||||
|
||||
wire sb_badsize = sbaccess > 3'h2;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
sbbusy <= 1'b0;
|
||||
sbbusyerror <= 1'b0;
|
||||
sbreadonaddr <= 1'b0;
|
||||
sbreadondata <= 1'b0;
|
||||
sbaccess <= 3'h0;
|
||||
sbautoincrement <= 1'b0;
|
||||
sberror <= 3'h0;
|
||||
sb_current_is_write <= 1'b0;
|
||||
end else if (!dmactive) begin
|
||||
sbbusy <= 1'b0;
|
||||
sbbusyerror <= 1'b0;
|
||||
sbreadonaddr <= 1'b0;
|
||||
sbreadondata <= 1'b0;
|
||||
sbaccess <= 3'h0;
|
||||
sbautoincrement <= 1'b0;
|
||||
sberror <= 3'h0;
|
||||
sb_current_is_write <= 1'b0;
|
||||
end else if (HAVE_SBA) begin
|
||||
if (dmi_write && dmi_regaddr == ADDR_SBCS) begin
|
||||
// Assume a transfer is not in progress when written (per spec)
|
||||
sbbusyerror <= sbbusyerror && !dmi_pwdata[22];
|
||||
sbreadonaddr <= dmi_pwdata[20];
|
||||
sbaccess <= dmi_pwdata[19:17];
|
||||
sbautoincrement <= dmi_pwdata[16];
|
||||
sbreadondata <= dmi_pwdata[15];
|
||||
sberror <= sberror & ~dmi_pwdata[14:12];
|
||||
end
|
||||
if (sbbusy) begin
|
||||
if (sb_access_illegal_when_busy) begin
|
||||
sbbusyerror <= 1'b1;
|
||||
end
|
||||
if (sbus_vld && sbus_rdy) begin
|
||||
sbbusy <= 1'b0;
|
||||
if (sbus_err) begin
|
||||
sberror <= SBERROR_BADADDR;
|
||||
end
|
||||
end
|
||||
end else if ((sb_want_start_read || sb_want_start_write) && ~|sberror && !sbbusyerror) begin
|
||||
if (sb_badsize) begin
|
||||
sberror <= SBERROR_BADSIZE;
|
||||
end else if (sb_badalign) begin
|
||||
sberror <= SBERROR_BADALIGN;
|
||||
end else begin
|
||||
sbbusy <= 1'b1;
|
||||
sb_current_is_write <= sb_want_start_write;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assign sbus_addr = sbaddress;
|
||||
assign sbus_write = sb_current_is_write;
|
||||
assign sbus_size = sbaccess[1:0];
|
||||
assign sbus_vld = sbbusy;
|
||||
|
||||
// Replicate byte lanes to handle naturally-aligned cases.
|
||||
assign sbus_wdata = sbaccess[1:0] == 2'b00 ? {4{sbdata[7:0]}} :
|
||||
sbaccess[1:0] == 2'b01 ? {2{sbdata[15:0]}} : sbdata;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Abstract command data registers
|
||||
|
||||
wire abstractcs_busy;
|
||||
|
||||
// The same data0 register is aliased as a CSR on all harts connected to this
|
||||
// DM. Cores may read data0 as a CSR when in debug mode, and may write it when:
|
||||
//
|
||||
// - That core is in debug mode, and...
|
||||
// - We are currently executing an abstract command on that core
|
||||
//
|
||||
// The DM can also read/write data0 at all times.
|
||||
|
||||
reg [XLEN-1:0] abstract_data0;
|
||||
|
||||
assign hart_data0_rdata = {N_HARTS{abstract_data0}};
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin: update_hart_data0
|
||||
reg signed [31:0] i;
|
||||
if (!rst_n) begin
|
||||
abstract_data0 <= {XLEN{1'b0}};
|
||||
end else if (!dmactive) begin
|
||||
abstract_data0 <= {XLEN{1'b0}};
|
||||
end else if (dmi_write && dmi_regaddr == ADDR_DATA0) begin
|
||||
abstract_data0 <= dmi_pwdata;
|
||||
end else begin
|
||||
for (i = 0; i < N_HARTS; i = i + 1) begin
|
||||
if (hartsel == i[W_HARTSEL-1:0] && hart_data0_wen[i] && hart_halted[i] && abstractcs_busy)
|
||||
abstract_data0 <= hart_data0_wdata[i * XLEN +: XLEN];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
reg [XLEN-1:0] progbuf0;
|
||||
reg [XLEN-1:0] progbuf1;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
progbuf0 <= {XLEN{1'b0}};
|
||||
progbuf1 <= {XLEN{1'b0}};
|
||||
end else if (!dmactive) begin
|
||||
progbuf0 <= {XLEN{1'b0}};
|
||||
progbuf1 <= {XLEN{1'b0}};
|
||||
end else if (dmi_write && !abstractcs_busy) begin
|
||||
if (dmi_regaddr == ADDR_PROGBUF0)
|
||||
progbuf0 <= dmi_pwdata;
|
||||
if (dmi_regaddr == ADDR_PROGBUF1)
|
||||
progbuf1 <= dmi_pwdata;
|
||||
end
|
||||
end
|
||||
|
||||
reg abstractauto_autoexecdata;
|
||||
reg [1:0] abstractauto_autoexecprogbuf;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
abstractauto_autoexecdata <= 1'b0;
|
||||
abstractauto_autoexecprogbuf <= 2'b00;
|
||||
end else if (!dmactive) begin
|
||||
abstractauto_autoexecdata <= 1'b0;
|
||||
abstractauto_autoexecprogbuf <= 2'b00;
|
||||
end else if (dmi_write && dmi_regaddr == ADDR_ABSTRACTAUTO) begin
|
||||
abstractauto_autoexecdata <= dmi_pwdata[0];
|
||||
abstractauto_autoexecprogbuf <= dmi_pwdata[17:16];
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Abstract command state machine
|
||||
|
||||
localparam W_STATE = 4;
|
||||
localparam S_IDLE = 4'd0;
|
||||
|
||||
localparam S_ISSUE_REGREAD = 4'd1;
|
||||
localparam S_ISSUE_REGWRITE = 4'd2;
|
||||
localparam S_ISSUE_REGEBREAK = 4'd3;
|
||||
localparam S_WAIT_REGEBREAK = 4'd4;
|
||||
|
||||
localparam S_ISSUE_PROGBUF0 = 4'd5;
|
||||
localparam S_ISSUE_PROGBUF1 = 4'd6;
|
||||
localparam S_ISSUE_IMPEBREAK = 4'd7;
|
||||
localparam S_WAIT_IMPEBREAK = 4'd8;
|
||||
|
||||
localparam CMDERR_OK = 3'h0;
|
||||
localparam CMDERR_BUSY = 3'h1;
|
||||
localparam CMDERR_UNSUPPORTED = 3'h2;
|
||||
localparam CMDERR_EXCEPTION = 3'h3;
|
||||
localparam CMDERR_HALTRESUME = 3'h4;
|
||||
|
||||
reg [2:0] abstractcs_cmderr;
|
||||
reg [2:0] abstractcs_cmderr_nxt;
|
||||
reg [W_STATE-1:0] acmd_state;
|
||||
reg [W_STATE-1:0] acmd_state_nxt;
|
||||
|
||||
assign abstractcs_busy = acmd_state != S_IDLE;
|
||||
|
||||
wire start_abstract_cmd = abstractcs_cmderr == CMDERR_OK && !abstractcs_busy && (
|
||||
(dmi_write && dmi_regaddr == ADDR_COMMAND) ||
|
||||
((dmi_write || dmi_read) && abstractauto_autoexecdata && dmi_regaddr == ADDR_DATA0) ||
|
||||
((dmi_write || dmi_read) && abstractauto_autoexecprogbuf[0] && dmi_regaddr == ADDR_PROGBUF0) ||
|
||||
((dmi_write || dmi_read) && abstractauto_autoexecprogbuf[1] && dmi_regaddr == ADDR_PROGBUF1)
|
||||
);
|
||||
|
||||
wire dmi_access_illegal_when_busy =
|
||||
(dmi_write && (
|
||||
dmi_regaddr == ADDR_ABSTRACTCS || dmi_regaddr == ADDR_COMMAND || dmi_regaddr == ADDR_ABSTRACTAUTO ||
|
||||
dmi_regaddr == ADDR_DATA0 || dmi_regaddr == ADDR_PROGBUF0 || dmi_regaddr == ADDR_PROGBUF1)) ||
|
||||
(dmi_read && (
|
||||
dmi_regaddr == ADDR_DATA0 || dmi_regaddr == ADDR_PROGBUF0 || dmi_regaddr == ADDR_PROGBUF1));
|
||||
|
||||
// Decode what acmd may be triggered on this cycle, and whether it is
|
||||
// supported -- command source may be a registered version of most recent
|
||||
// command (if abstractauto is used) or a fresh command off the bus. We don't
|
||||
// register the entire write data; repeats of unsupported commands are
|
||||
// detected by just registering that the last written command was
|
||||
// unsupported.
|
||||
|
||||
wire acmd_new = dmi_write && dmi_regaddr == ADDR_COMMAND;
|
||||
|
||||
wire acmd_new_postexec = dmi_pwdata[18];
|
||||
wire acmd_new_transfer = dmi_pwdata[17];
|
||||
wire acmd_new_write = dmi_pwdata[16];
|
||||
wire [4:0] acmd_new_regno = dmi_pwdata[4:0];
|
||||
|
||||
// Note: regno and aarsize are permitted to have otherwise-invalid values if
|
||||
// the transfer flag is not set.
|
||||
wire acmd_new_unsupported =
|
||||
(dmi_pwdata[31:24] != 8'h00 ) || // Only Access Register command supported
|
||||
(dmi_pwdata[22:20] != 3'h2 && acmd_new_transfer) || // Must be 32 bits in size
|
||||
(dmi_pwdata[19] ) || // aarpostincrement not supported
|
||||
(dmi_pwdata[15:12] != 4'h1 && acmd_new_transfer) || // Only core register access supported
|
||||
(dmi_pwdata[11:5] != 7'h0 && acmd_new_transfer); // Only GPRs supported
|
||||
|
||||
reg acmd_prev_postexec;
|
||||
reg acmd_prev_transfer;
|
||||
reg acmd_prev_write;
|
||||
reg [4:0] acmd_prev_regno;
|
||||
reg acmd_prev_unsupported;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
acmd_prev_postexec <= 1'b0;
|
||||
acmd_prev_transfer <= 1'b0;
|
||||
acmd_prev_write <= 1'b0;
|
||||
acmd_prev_regno <= 5'h0;
|
||||
acmd_prev_unsupported <= 1'b1;
|
||||
end else if (!dmactive) begin
|
||||
acmd_prev_postexec <= 1'b0;
|
||||
acmd_prev_transfer <= 1'b0;
|
||||
acmd_prev_write <= 1'b0;
|
||||
acmd_prev_regno <= 5'h0;
|
||||
acmd_prev_unsupported <= 1'b1;
|
||||
end else if (start_abstract_cmd && acmd_new) begin
|
||||
acmd_prev_postexec <= acmd_new_postexec;
|
||||
acmd_prev_transfer <= acmd_new_transfer;
|
||||
acmd_prev_write <= acmd_new_write;
|
||||
acmd_prev_regno <= acmd_new_regno;
|
||||
acmd_prev_unsupported <= acmd_new_unsupported;
|
||||
end
|
||||
end
|
||||
|
||||
wire acmd_postexec = acmd_new ? acmd_new_postexec : acmd_prev_postexec ;
|
||||
wire acmd_transfer = acmd_new ? acmd_new_transfer : acmd_prev_transfer ;
|
||||
wire acmd_write = acmd_new ? acmd_new_write : acmd_prev_write ;
|
||||
wire [4:0] acmd_regno = acmd_new ? acmd_new_regno : acmd_prev_regno ;
|
||||
wire acmd_unsupported = acmd_new ? acmd_new_unsupported : acmd_prev_unsupported;
|
||||
|
||||
always @ (*) begin
|
||||
// Default: no state change
|
||||
acmd_state_nxt = acmd_state;
|
||||
abstractcs_cmderr_nxt = abstractcs_cmderr;
|
||||
|
||||
if (dmi_write && dmi_regaddr == ADDR_ABSTRACTCS && !abstractcs_busy)
|
||||
abstractcs_cmderr_nxt = abstractcs_cmderr & ~dmi_pwdata[10:8];
|
||||
if (abstractcs_cmderr == CMDERR_OK && abstractcs_busy && dmi_access_illegal_when_busy)
|
||||
abstractcs_cmderr_nxt = CMDERR_BUSY;
|
||||
if (acmd_state != S_IDLE && hart_instr_caught_exception[hartsel])
|
||||
abstractcs_cmderr_nxt = CMDERR_EXCEPTION;
|
||||
|
||||
case (acmd_state)
|
||||
S_IDLE: begin
|
||||
if (start_abstract_cmd) begin
|
||||
if (!hart_halted[hartsel] || !hart_available[hartsel]) begin
|
||||
abstractcs_cmderr_nxt = CMDERR_HALTRESUME;
|
||||
end else if (acmd_unsupported) begin
|
||||
abstractcs_cmderr_nxt = CMDERR_UNSUPPORTED;
|
||||
end else begin
|
||||
if (acmd_transfer && acmd_write)
|
||||
acmd_state_nxt = S_ISSUE_REGWRITE;
|
||||
else if (acmd_transfer && !acmd_write)
|
||||
acmd_state_nxt = S_ISSUE_REGREAD;
|
||||
else if (acmd_postexec)
|
||||
acmd_state_nxt = S_ISSUE_PROGBUF0;
|
||||
else
|
||||
acmd_state_nxt = S_IDLE;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
S_ISSUE_REGREAD: begin
|
||||
if (hart_instr_data_rdy[hartsel])
|
||||
acmd_state_nxt = S_ISSUE_REGEBREAK;
|
||||
end
|
||||
S_ISSUE_REGWRITE: begin
|
||||
if (hart_instr_data_rdy[hartsel])
|
||||
acmd_state_nxt = S_ISSUE_REGEBREAK;
|
||||
end
|
||||
S_ISSUE_REGEBREAK: begin
|
||||
if (hart_instr_data_rdy[hartsel])
|
||||
acmd_state_nxt = S_WAIT_REGEBREAK;
|
||||
end
|
||||
S_WAIT_REGEBREAK: begin
|
||||
if (hart_instr_caught_ebreak[hartsel]) begin
|
||||
if (acmd_prev_postexec)
|
||||
acmd_state_nxt = S_ISSUE_PROGBUF0;
|
||||
else
|
||||
acmd_state_nxt = S_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
S_ISSUE_PROGBUF0: begin
|
||||
if (hart_instr_data_rdy[hartsel])
|
||||
acmd_state_nxt = S_ISSUE_PROGBUF1;
|
||||
end
|
||||
S_ISSUE_PROGBUF1: begin
|
||||
if (hart_instr_caught_exception[hartsel] || hart_instr_caught_ebreak[hartsel]) begin
|
||||
acmd_state_nxt = S_IDLE;
|
||||
end else if (hart_instr_data_rdy[hartsel]) begin
|
||||
acmd_state_nxt = S_ISSUE_IMPEBREAK;
|
||||
end
|
||||
end
|
||||
S_ISSUE_IMPEBREAK: begin
|
||||
if (hart_instr_caught_exception[hartsel] || hart_instr_caught_ebreak[hartsel]) begin
|
||||
acmd_state_nxt = S_IDLE;
|
||||
end else if (hart_instr_data_rdy[hartsel]) begin
|
||||
acmd_state_nxt = S_WAIT_IMPEBREAK;
|
||||
end
|
||||
end
|
||||
S_WAIT_IMPEBREAK: begin
|
||||
if (hart_instr_caught_exception[hartsel] || hart_instr_caught_ebreak[hartsel]) begin
|
||||
acmd_state_nxt = S_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
// Unreachable
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
abstractcs_cmderr <= CMDERR_OK;
|
||||
acmd_state <= S_IDLE;
|
||||
end else if (!dmactive) begin
|
||||
abstractcs_cmderr <= CMDERR_OK;
|
||||
acmd_state <= S_IDLE;
|
||||
end else begin
|
||||
abstractcs_cmderr <= abstractcs_cmderr_nxt;
|
||||
acmd_state <= acmd_state_nxt;
|
||||
end
|
||||
end
|
||||
|
||||
wire [N_HARTS-1:0] hart_instr_data_vld_nxt = {{N_HARTS-1{1'b0}},
|
||||
acmd_state_nxt == S_ISSUE_REGREAD || acmd_state_nxt == S_ISSUE_REGWRITE || acmd_state_nxt == S_ISSUE_REGEBREAK ||
|
||||
acmd_state_nxt == S_ISSUE_PROGBUF0 || acmd_state_nxt == S_ISSUE_PROGBUF1 || acmd_state_nxt == S_ISSUE_IMPEBREAK
|
||||
} << hartsel;
|
||||
|
||||
wire [31:0] hart_instr_data_nxt =
|
||||
acmd_state_nxt == S_ISSUE_REGWRITE ? 32'hbff02073 | {20'd0, acmd_regno, 7'd0} : // csrr xx, dmdata0
|
||||
acmd_state_nxt == S_ISSUE_REGREAD ? 32'hbff01073 | {12'd0, acmd_regno, 15'd0} : // csrw dmdata0, xx
|
||||
acmd_state_nxt == S_ISSUE_PROGBUF0 ? progbuf0 :
|
||||
acmd_state_nxt == S_ISSUE_PROGBUF1 ? progbuf1 :
|
||||
32'h00100073; // ebreak
|
||||
|
||||
reg [31:0] hart_instr_data_reg;
|
||||
assign hart_instr_data = {N_HARTS{hart_instr_data_reg}};
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
hart_instr_data_vld <= 1'b0;
|
||||
hart_instr_data_reg <= 32'h00000000;
|
||||
end else begin
|
||||
hart_instr_data_vld <= hart_instr_data_vld_nxt;
|
||||
if (hart_instr_data_vld_nxt) begin
|
||||
hart_instr_data_reg <= hart_instr_data_nxt;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Status helper functions
|
||||
|
||||
function status_any;
|
||||
input [N_HARTS-1:0] status_mask;
|
||||
begin
|
||||
status_any = status_mask[hartsel] || (hasel && |(status_mask & hart_array_mask));
|
||||
end
|
||||
endfunction
|
||||
|
||||
function status_all;
|
||||
input [N_HARTS-1:0] status_mask;
|
||||
begin
|
||||
status_all = status_mask[hartsel] && (!hasel || ~|(~status_mask & hart_array_mask));
|
||||
end
|
||||
endfunction
|
||||
|
||||
function [1:0] status_all_any;
|
||||
input [N_HARTS-1:0] status_mask;
|
||||
begin
|
||||
status_all_any = {
|
||||
status_all(status_mask),
|
||||
status_any(status_mask)
|
||||
};
|
||||
end
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DMI read data mux
|
||||
|
||||
always @ (*) begin
|
||||
case (dmi_regaddr)
|
||||
ADDR_DATA0: dmi_prdata = abstract_data0;
|
||||
ADDR_DMCONTROL: dmi_prdata = {
|
||||
1'b0, // haltreq is a W-only field
|
||||
1'b0, // resumereq is a W1 field
|
||||
status_any(dmcontrol_hartreset),
|
||||
1'b0, // ackhavereset is a W1 field
|
||||
1'b0, // reserved
|
||||
hasel,
|
||||
{{10-W_HARTSEL{1'b0}}, hartsel}, // hartsello
|
||||
10'h0, // hartselhi
|
||||
2'h0, // reserved
|
||||
2'h0, // set/clrresethaltreq are W1 fields
|
||||
dmcontrol_ndmreset,
|
||||
dmactive
|
||||
};
|
||||
ADDR_DMSTATUS: dmi_prdata = {
|
||||
9'h0, // reserved
|
||||
1'b1, // impebreak = 1
|
||||
2'h0, // reserved
|
||||
status_all_any(dmstatus_havereset), // allhavereset, anyhavereset
|
||||
status_all_any(dmstatus_resumeack), // allresumeack, anyresumeack
|
||||
hartsel >= N_HARTS && !(hasel && |hart_array_mask), // allnonexistent
|
||||
hartsel >= N_HARTS, // anynonexistent
|
||||
status_all_any(~hart_available), // allunavail, anyunavail
|
||||
status_all_any(hart_running & hart_available), // allrunning, anyrunning
|
||||
status_all_any(hart_halted & hart_available), // allhalted, anyhalted
|
||||
1'b1, // authenticated
|
||||
1'b0, // authbusy
|
||||
1'b1, // hasresethaltreq = 1 (we do support it)
|
||||
1'b0, // confstrptrvalid
|
||||
4'd2 // version = 2: RISC-V debug spec 0.13.2
|
||||
};
|
||||
ADDR_HARTINFO: dmi_prdata = {
|
||||
8'h0, // reserved
|
||||
4'h0, // nscratch = 0
|
||||
3'h0, // reserved
|
||||
1'b0, // dataccess = 0, data0 is mapped to each hart's CSR space
|
||||
4'h1, // datasize = 1, a single data CSR (data0) is available
|
||||
12'hbff // dataaddr, placed at the top of the M-custom space since
|
||||
// the spec doesn't reserve a location for it.
|
||||
};
|
||||
ADDR_HALTSUM0: dmi_prdata = {
|
||||
{XLEN - N_HARTS{1'b0}},
|
||||
hart_halted & hart_available
|
||||
};
|
||||
ADDR_HALTSUM1: dmi_prdata = {
|
||||
{XLEN - 1{1'b0}},
|
||||
|(hart_halted & hart_available)
|
||||
};
|
||||
ADDR_HAWINDOWSEL: dmi_prdata = 32'h00000000;
|
||||
ADDR_HAWINDOW: dmi_prdata = {
|
||||
{32-N_HARTS{1'b0}},
|
||||
hart_array_mask
|
||||
};
|
||||
ADDR_ABSTRACTCS: dmi_prdata = {
|
||||
3'h0, // reserved
|
||||
5'd2, // progbufsize = 2
|
||||
11'h0, // reserved
|
||||
abstractcs_busy,
|
||||
1'b0,
|
||||
abstractcs_cmderr,
|
||||
4'h0,
|
||||
4'd1 // datacount = 1
|
||||
};
|
||||
ADDR_ABSTRACTAUTO: dmi_prdata = {
|
||||
14'h0,
|
||||
abstractauto_autoexecprogbuf, // only progbuf0,1 present
|
||||
15'h0,
|
||||
abstractauto_autoexecdata // only data0 present
|
||||
};
|
||||
ADDR_SBCS: dmi_prdata = {
|
||||
3'h1, // version = 1
|
||||
6'h00,
|
||||
sbbusyerror,
|
||||
sbbusy,
|
||||
sbreadonaddr,
|
||||
sbaccess,
|
||||
sbautoincrement,
|
||||
sbreadondata,
|
||||
sberror,
|
||||
7'h20, // sbasize = 32
|
||||
5'b00111 // 8, 16, 32-bit transfers supported
|
||||
} & {32{|HAVE_SBA}};
|
||||
ADDR_SBDATA0: dmi_prdata = sbdata & {32{|HAVE_SBA}};
|
||||
ADDR_SBADDRESS0: dmi_prdata = sbaddress & {32{|HAVE_SBA}};
|
||||
ADDR_CONFSTRPTR0: dmi_prdata = 32'h4c296328;
|
||||
ADDR_CONFSTRPTR1: dmi_prdata = 32'h20656b75;
|
||||
ADDR_CONFSTRPTR2: dmi_prdata = 32'h6e657257;
|
||||
ADDR_CONFSTRPTR3: dmi_prdata = 32'h31322720;
|
||||
ADDR_NEXTDM: dmi_prdata = NEXT_DM_ADDR;
|
||||
ADDR_PROGBUF0: dmi_prdata = progbuf0;
|
||||
ADDR_PROGBUF1: dmi_prdata = progbuf1;
|
||||
default: dmi_prdata = {XLEN{1'b0}};
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,74 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Standalone bus shim for connecting the DM's System Bus Access to AHB
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_sbus_to_ahb #(
|
||||
parameter W_ADDR = 32,
|
||||
parameter W_DATA = 32
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
|
||||
input wire [W_ADDR-1:0] sbus_addr,
|
||||
input wire sbus_write,
|
||||
input wire [1:0] sbus_size,
|
||||
input wire sbus_vld,
|
||||
output wire sbus_rdy,
|
||||
output wire sbus_err,
|
||||
input wire [W_DATA-1:0] sbus_wdata,
|
||||
output wire [W_DATA-1:0] sbus_rdata,
|
||||
|
||||
output wire [W_ADDR-1:0] ahblm_haddr,
|
||||
output wire ahblm_hwrite,
|
||||
output wire [1:0] ahblm_htrans,
|
||||
output wire [2:0] ahblm_hsize,
|
||||
output wire [2:0] ahblm_hburst,
|
||||
output wire [3:0] ahblm_hprot,
|
||||
output wire ahblm_hmastlock,
|
||||
input wire ahblm_hready,
|
||||
input wire ahblm_hresp,
|
||||
output wire [W_DATA-1:0] ahblm_hwdata,
|
||||
input wire [W_DATA-1:0] ahblm_hrdata
|
||||
);
|
||||
|
||||
// Most signals are simple tie-throughs
|
||||
|
||||
assign ahblm_haddr = sbus_addr;
|
||||
assign ahblm_hwrite = sbus_write;
|
||||
assign ahblm_hsize = {1'b0, sbus_size};
|
||||
assign ahblm_hwdata = sbus_wdata;
|
||||
|
||||
// HPROT = noncacheable nonbufferable privileged data access:
|
||||
assign ahblm_hprot = 4'b0011;
|
||||
assign ahblm_hmastlock = 1'b0;
|
||||
assign ahblm_hburst = 3'h0;
|
||||
|
||||
assign sbus_err = ahblm_hresp;
|
||||
assign sbus_rdata = ahblm_hrdata;
|
||||
|
||||
// Handshaking
|
||||
|
||||
reg dph_active;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
dph_active <= 1'b0;
|
||||
end else if (ahblm_hready) begin
|
||||
dph_active <= ahblm_htrans[1];
|
||||
end
|
||||
end
|
||||
|
||||
assign ahblm_htrans = sbus_vld && !dph_active ? 2'b10 : 2'b00;
|
||||
|
||||
assign sbus_rdy = ahblm_hready && dph_active;
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,6 @@
|
||||
file hazard3_ecp5_jtag_dtm.v
|
||||
file hazard3_jtag_dtm_core.v
|
||||
|
||||
file ../cdc/hazard3_apb_async_bridge.v
|
||||
file ../cdc/hazard3_reset_sync.v
|
||||
file ../cdc/hazard3_sync_1bit.v
|
||||
@@ -0,0 +1,194 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// The ECP5 JTAGG primitive (yes that is the correct spelling) allows you to
|
||||
// add two custom DRs to the FPGA's chip TAP, selected using the 8-bit ER1
|
||||
// (0x32) and ER2 (0x38) instructions.
|
||||
//
|
||||
// Brian Swetland pointed out on Twitter that the standard RISC-V JTAG-DTM
|
||||
// only uses two DRs (DTMCS and DMI), besides the standard IDCODE and BYPASS
|
||||
// which are provided already by the ECP5 TAP. This file instantiates the
|
||||
// guts of Hazard3's standard JTAG-DTM and connects the DTMCS and DMI
|
||||
// registers to the JTAGG primitive's ER1/ER2 DRs.
|
||||
//
|
||||
// The exciting part is that upstream OpenOCD already allows you to set the IR
|
||||
// length *and* set custom DTMCS/DMI IR values for RISC-V JTAG DTMs. This
|
||||
// means with the right config file, you can access a debug module hung from
|
||||
// the ECP5 TAP in this fashion using only upstream OpenOCD and gdb.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_ecp5_jtag_dtm #(
|
||||
parameter DTMCS_IDLE_HINT = 3'd4,
|
||||
parameter W_PADDR = 9,
|
||||
parameter ABITS = W_PADDR - 2 // do not modify
|
||||
) (
|
||||
// This is synchronous to TCK and asserted for one TCK cycle only
|
||||
output wire dmihardreset_req,
|
||||
|
||||
// Bus clock + reset for Debug Module Interface
|
||||
input wire clk_dmi,
|
||||
input wire rst_n_dmi,
|
||||
|
||||
// Debug Module Interface (APB)
|
||||
output wire dmi_psel,
|
||||
output wire dmi_penable,
|
||||
output wire dmi_pwrite,
|
||||
output wire [W_PADDR-1:0] dmi_paddr,
|
||||
output wire [31:0] dmi_pwdata,
|
||||
input wire [31:0] dmi_prdata,
|
||||
input wire dmi_pready,
|
||||
input wire dmi_pslverr
|
||||
);
|
||||
|
||||
// Signals to/from the ECP5 TAP
|
||||
|
||||
wire jtdo2;
|
||||
wire jtdo1;
|
||||
wire jtdi;
|
||||
wire jtck_posedge_dont_use;
|
||||
wire jshift;
|
||||
wire jupdate;
|
||||
wire jrst_n;
|
||||
wire jce2;
|
||||
wire jce1;
|
||||
|
||||
JTAGG jtag_u (
|
||||
.JTDO2 (jtdo2),
|
||||
.JTDO1 (jtdo1),
|
||||
.JTDI (jtdi),
|
||||
.JTCK (jtck_posedge_dont_use),
|
||||
.JRTI2 (/* unused */),
|
||||
.JRTI1 (/* unused */),
|
||||
.JSHIFT (jshift),
|
||||
.JUPDATE (jupdate),
|
||||
.JRSTN (jrst_n),
|
||||
.JCE2 (jce2),
|
||||
.JCE1 (jce1)
|
||||
);
|
||||
|
||||
// JTAGG primitive asserts its signals synchronously to JTCK's posedge, but
|
||||
// you get weird and inconsistent results if you try to consume them
|
||||
// synchronously on JTCK's posedge, possibly due to a lack of hold
|
||||
// constraints in nextpnr.
|
||||
//
|
||||
// A quick hack is to move the sampling onto the negedge of the clock. This
|
||||
// then creates more problems because we would be running our shift logic on
|
||||
// a different edge from the control + CDC logic in the DTM core.
|
||||
//
|
||||
// So, even worse hack, move all our JTAG-domain logic onto the negedge
|
||||
// (or near enough) by inverting the clock.
|
||||
|
||||
wire jtck = !jtck_posedge_dont_use;
|
||||
|
||||
localparam W_DR_SHIFT = ABITS + 32 + 2;
|
||||
|
||||
reg core_dr_wen;
|
||||
reg core_dr_ren;
|
||||
reg core_dr_sel_dmi_ndtmcs;
|
||||
reg dr_shift_en;
|
||||
wire [W_DR_SHIFT-1:0] core_dr_wdata;
|
||||
wire [W_DR_SHIFT-1:0] core_dr_rdata;
|
||||
|
||||
// Decode our shift controls from the interesting ECP5 ones, and re-register
|
||||
// onto JTCK negedge (our posedge). Note without re-registering we observe
|
||||
// them a half-cycle (effectively one cycle) too early. This is another
|
||||
// consequence of the stupid JTDI thing
|
||||
|
||||
always @ (posedge jtck or negedge jrst_n) begin
|
||||
if (!jrst_n) begin
|
||||
core_dr_sel_dmi_ndtmcs <= 1'b0;
|
||||
core_dr_wen <= 1'b0;
|
||||
core_dr_ren <= 1'b0;
|
||||
dr_shift_en <= 1'b0;
|
||||
end else begin
|
||||
if (jce1 || jce2)
|
||||
core_dr_sel_dmi_ndtmcs <= jce2;
|
||||
core_dr_ren <= (jce1 || jce2) && !jshift;
|
||||
core_dr_wen <= jupdate;
|
||||
dr_shift_en <= jshift;
|
||||
end
|
||||
end
|
||||
|
||||
reg [W_DR_SHIFT-1:0] dr_shift;
|
||||
assign core_dr_wdata = dr_shift;
|
||||
|
||||
always @ (posedge jtck or negedge jrst_n) begin
|
||||
if (!jrst_n) begin
|
||||
dr_shift <= {W_DR_SHIFT{1'b0}};
|
||||
end else if (core_dr_ren) begin
|
||||
dr_shift <= core_dr_rdata;
|
||||
end else if (dr_shift_en) begin
|
||||
dr_shift <= {jtdi, dr_shift[W_DR_SHIFT-1:1]};
|
||||
if (!core_dr_sel_dmi_ndtmcs)
|
||||
dr_shift[31] <= jtdi;
|
||||
end
|
||||
end
|
||||
|
||||
// Not documented on ECP5: as well as the posedge flop on JTDI, the ECP5 puts
|
||||
// a negedge flop on JTDO1, JTDO2. (Conjecture based on dicking around with a
|
||||
// logic analyser.) To get JTDOx to appear with the same timing as our shifter
|
||||
// LSB (which we update on every JTCK negedge) we:
|
||||
//
|
||||
// - Register the LSB of the *next* value of dr_shift on the JTCK posedge, so
|
||||
// half a cycle earlier than the actual dr_shift update
|
||||
//
|
||||
// - This then gets re-registered with the pointless JTDO negedge flops, so
|
||||
// that it appears with the same timing as our DR shifter update.
|
||||
|
||||
reg dr_shift_next_halfcycle;
|
||||
always @ (negedge jtck or negedge jrst_n) begin
|
||||
if (!jrst_n) begin
|
||||
dr_shift_next_halfcycle <= 1'b0;
|
||||
end else begin
|
||||
dr_shift_next_halfcycle <=
|
||||
core_dr_ren ? core_dr_rdata[0] :
|
||||
dr_shift_en ? dr_shift[1] : dr_shift[0];
|
||||
end
|
||||
end
|
||||
|
||||
// We have only a single shifter for the ER1 and ER2 chains, so these are tied
|
||||
// together:
|
||||
|
||||
assign jtdo1 = dr_shift_next_halfcycle;
|
||||
assign jtdo2 = dr_shift_next_halfcycle;
|
||||
|
||||
// The actual DTM is in here:
|
||||
|
||||
hazard3_jtag_dtm_core #(
|
||||
.DTMCS_IDLE_HINT (DTMCS_IDLE_HINT),
|
||||
.W_ADDR (ABITS)
|
||||
) inst_hazard3_jtag_dtm_core (
|
||||
.tck (jtck),
|
||||
.trst_n (jrst_n),
|
||||
|
||||
.clk_dmi (clk_dmi),
|
||||
.rst_n_dmi (rst_n_dmi),
|
||||
|
||||
.dr_wen (core_dr_wen),
|
||||
.dr_ren (core_dr_ren),
|
||||
.dr_sel_dmi_ndtmcs (core_dr_sel_dmi_ndtmcs),
|
||||
.dr_wdata (core_dr_wdata),
|
||||
.dr_rdata (core_dr_rdata),
|
||||
|
||||
.dmihardreset_req (dmihardreset_req),
|
||||
|
||||
.dmi_psel (dmi_psel),
|
||||
.dmi_penable (dmi_penable),
|
||||
.dmi_pwrite (dmi_pwrite),
|
||||
.dmi_paddr (dmi_paddr[W_PADDR-1:2]),
|
||||
.dmi_pwdata (dmi_pwdata),
|
||||
.dmi_prdata (dmi_prdata),
|
||||
.dmi_pready (dmi_pready),
|
||||
.dmi_pslverr (dmi_pslverr)
|
||||
);
|
||||
|
||||
assign dmi_paddr[1:0] = 2'b00;
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,6 @@
|
||||
file hazard3_jtag_dtm.v
|
||||
file hazard3_jtag_dtm_core.v
|
||||
|
||||
file ../cdc/hazard3_apb_async_bridge.v
|
||||
file ../cdc/hazard3_reset_sync.v
|
||||
file ../cdc/hazard3_sync_1bit.v
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Implementation of standard RISC-V JTAG-DTM with an APB Debug Module
|
||||
// Interface. The TAP itself is clocked directly by JTAG TCK; a clock
|
||||
// crossing is instantiated internally between the TCK domain and the DMI bus
|
||||
// clock domain.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_jtag_dtm #(
|
||||
parameter IDCODE = 32'h0000_0001,
|
||||
parameter DTMCS_IDLE_HINT = 3'd4,
|
||||
parameter W_PADDR = 9,
|
||||
parameter ABITS = W_PADDR - 2 // do not modify
|
||||
) (
|
||||
// Standard JTAG signals -- the JTAG hardware is clocked directly by TCK.
|
||||
input wire tck,
|
||||
input wire trst_n,
|
||||
input wire tms,
|
||||
input wire tdi,
|
||||
output reg tdo,
|
||||
|
||||
// This is synchronous to TCK and asserted for one TCK cycle only
|
||||
output wire dmihardreset_req,
|
||||
|
||||
// Bus clock + reset for Debug Module Interface
|
||||
input wire clk_dmi,
|
||||
input wire rst_n_dmi,
|
||||
|
||||
// Debug Module Interface (APB)
|
||||
output wire dmi_psel,
|
||||
output wire dmi_penable,
|
||||
output wire dmi_pwrite,
|
||||
output wire [W_PADDR-1:0] dmi_paddr,
|
||||
output wire [31:0] dmi_pwdata,
|
||||
input wire [31:0] dmi_prdata,
|
||||
input wire dmi_pready,
|
||||
input wire dmi_pslverr
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TAP state machine
|
||||
|
||||
reg [3:0] tap_state;
|
||||
localparam S_RESET = 4'd0;
|
||||
localparam S_RUN_IDLE = 4'd1;
|
||||
localparam S_SELECT_DR = 4'd2;
|
||||
localparam S_CAPTURE_DR = 4'd3;
|
||||
localparam S_SHIFT_DR = 4'd4;
|
||||
localparam S_EXIT1_DR = 4'd5;
|
||||
localparam S_PAUSE_DR = 4'd6;
|
||||
localparam S_EXIT2_DR = 4'd7;
|
||||
localparam S_UPDATE_DR = 4'd8;
|
||||
localparam S_SELECT_IR = 4'd9;
|
||||
localparam S_CAPTURE_IR = 4'd10;
|
||||
localparam S_SHIFT_IR = 4'd11;
|
||||
localparam S_EXIT1_IR = 4'd12;
|
||||
localparam S_PAUSE_IR = 4'd13;
|
||||
localparam S_EXIT2_IR = 4'd14;
|
||||
localparam S_UPDATE_IR = 4'd15;
|
||||
|
||||
always @ (posedge tck or negedge trst_n) begin
|
||||
if (!trst_n) begin
|
||||
tap_state <= S_RESET;
|
||||
end else case(tap_state)
|
||||
S_RESET : tap_state <= tms ? S_RESET : S_RUN_IDLE ;
|
||||
S_RUN_IDLE : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ;
|
||||
|
||||
S_SELECT_DR : tap_state <= tms ? S_SELECT_IR : S_CAPTURE_DR;
|
||||
S_CAPTURE_DR : tap_state <= tms ? S_EXIT1_DR : S_SHIFT_DR ;
|
||||
S_SHIFT_DR : tap_state <= tms ? S_EXIT1_DR : S_SHIFT_DR ;
|
||||
S_EXIT1_DR : tap_state <= tms ? S_UPDATE_DR : S_PAUSE_DR ;
|
||||
S_PAUSE_DR : tap_state <= tms ? S_EXIT2_DR : S_PAUSE_DR ;
|
||||
S_EXIT2_DR : tap_state <= tms ? S_UPDATE_DR : S_SHIFT_DR ;
|
||||
S_UPDATE_DR : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ;
|
||||
|
||||
S_SELECT_IR : tap_state <= tms ? S_RESET : S_CAPTURE_IR;
|
||||
S_CAPTURE_IR : tap_state <= tms ? S_EXIT1_IR : S_SHIFT_IR ;
|
||||
S_SHIFT_IR : tap_state <= tms ? S_EXIT1_IR : S_SHIFT_IR ;
|
||||
S_EXIT1_IR : tap_state <= tms ? S_UPDATE_IR : S_PAUSE_IR ;
|
||||
S_PAUSE_IR : tap_state <= tms ? S_EXIT2_IR : S_PAUSE_IR ;
|
||||
S_EXIT2_IR : tap_state <= tms ? S_UPDATE_IR : S_SHIFT_IR ;
|
||||
S_UPDATE_IR : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ;
|
||||
endcase
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Instruction register
|
||||
|
||||
localparam W_IR = 5;
|
||||
// All other encodings behave as BYPASS:
|
||||
localparam IR_IDCODE = 5'h01;
|
||||
localparam IR_DTMCS = 5'h10;
|
||||
localparam IR_DMI = 5'h11;
|
||||
|
||||
reg [W_IR-1:0] ir_shift;
|
||||
reg [W_IR-1:0] ir;
|
||||
|
||||
always @ (posedge tck or negedge trst_n) begin
|
||||
if (!trst_n) begin
|
||||
ir_shift <= {W_IR{1'b0}};
|
||||
ir <= IR_IDCODE;
|
||||
end else if (tap_state == S_RESET) begin
|
||||
ir_shift <= {W_IR{1'b0}};
|
||||
ir <= IR_IDCODE;
|
||||
end else if (tap_state == S_CAPTURE_IR) begin
|
||||
ir_shift <= ir;
|
||||
end else if (tap_state == S_SHIFT_IR) begin
|
||||
ir_shift <= {tdi, ir_shift[W_IR-1:1]};
|
||||
end else if (tap_state == S_UPDATE_IR) begin
|
||||
ir <= ir_shift;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Data registers
|
||||
|
||||
// Shift register is sized to largest DR, which is DMI:
|
||||
// {addr[7:0], data[31:0], op[1:0]}
|
||||
localparam W_DR_SHIFT = ABITS + 32 + 2;
|
||||
|
||||
reg [W_DR_SHIFT-1:0] dr_shift;
|
||||
|
||||
// Signals to/from the DTM core, which implements the DTMCS and DMI registers
|
||||
wire core_dr_wen;
|
||||
wire core_dr_ren;
|
||||
wire core_dr_sel_dmi_ndtmcs;
|
||||
wire [W_DR_SHIFT-1:0] core_dr_wdata;
|
||||
wire [W_DR_SHIFT-1:0] core_dr_rdata;
|
||||
|
||||
always @ (posedge tck or negedge trst_n) begin
|
||||
if (!trst_n) begin
|
||||
dr_shift <= {W_DR_SHIFT{1'b0}};
|
||||
end else if (tap_state == S_SHIFT_DR) begin
|
||||
dr_shift <= {tdi, dr_shift[W_DR_SHIFT-1:1]};
|
||||
// Shorten DR shift chain according to IR
|
||||
if (ir == IR_DMI)
|
||||
dr_shift[W_DR_SHIFT - 1] <= tdi;
|
||||
else if (ir == IR_IDCODE || ir == IR_DTMCS)
|
||||
dr_shift[31] <= tdi;
|
||||
else // BYPASS
|
||||
dr_shift[0] <= tdi;
|
||||
end else if (tap_state == S_CAPTURE_DR) begin
|
||||
if (ir == IR_DMI || ir == IR_DTMCS) begin
|
||||
dr_shift <= core_dr_rdata;
|
||||
end else if (ir == IR_IDCODE) begin
|
||||
dr_shift <= {{W_DR_SHIFT-32{1'b0}}, IDCODE};
|
||||
end else begin // BYPASS
|
||||
dr_shift <= {W_DR_SHIFT{1'b0}};
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Must retime shift data onto negedge before presenting on TDO
|
||||
|
||||
always @ (negedge tck or negedge trst_n) begin
|
||||
if (!trst_n) begin
|
||||
tdo <= 1'b0;
|
||||
end else begin
|
||||
tdo <= tap_state == S_SHIFT_IR ? ir_shift[0] :
|
||||
tap_state == S_SHIFT_DR ? dr_shift[0] : 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Core logic and bus interface
|
||||
|
||||
assign core_dr_sel_dmi_ndtmcs = ir == IR_DMI;
|
||||
assign core_dr_wen = (ir == IR_DMI || ir == IR_DTMCS) && tap_state == S_UPDATE_DR;
|
||||
assign core_dr_ren = (ir == IR_DMI || ir == IR_DTMCS) && tap_state == S_CAPTURE_DR;
|
||||
|
||||
assign core_dr_wdata = dr_shift;
|
||||
|
||||
hazard3_jtag_dtm_core #(
|
||||
.DTMCS_IDLE_HINT (DTMCS_IDLE_HINT),
|
||||
.W_ADDR (ABITS)
|
||||
) dtm_core (
|
||||
.tck (tck),
|
||||
.trst_n (trst_n),
|
||||
.clk_dmi (clk_dmi),
|
||||
.rst_n_dmi (rst_n_dmi),
|
||||
|
||||
.dmihardreset_req (dmihardreset_req),
|
||||
|
||||
.dr_wen (core_dr_wen),
|
||||
.dr_ren (core_dr_ren),
|
||||
.dr_sel_dmi_ndtmcs (core_dr_sel_dmi_ndtmcs),
|
||||
.dr_wdata (core_dr_wdata),
|
||||
.dr_rdata (core_dr_rdata),
|
||||
|
||||
.dmi_psel (dmi_psel),
|
||||
.dmi_penable (dmi_penable),
|
||||
.dmi_pwrite (dmi_pwrite),
|
||||
.dmi_paddr (dmi_paddr[W_PADDR-1:2]),
|
||||
.dmi_pwdata (dmi_pwdata),
|
||||
.dmi_prdata (dmi_prdata),
|
||||
.dmi_pready (dmi_pready),
|
||||
.dmi_pslverr (dmi_pslverr)
|
||||
);
|
||||
|
||||
assign dmi_paddr[1:0] = 2'b00;
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,185 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// DTMCS + DMI control logic, bus interface and bus clock domain crossing for
|
||||
// a standard RISC-V APB JTAG-DTM. Essentially everything apart from the
|
||||
// actual TAP controller, IR and shift registers. Instantiated by
|
||||
// hazard3_jtag_dtm.v.
|
||||
//
|
||||
// This core logic can be reused and connected to some other serial transport
|
||||
// or, for example, the ECP5 JTAGG primitive (see hazard5_ecp5_jtag_dtm.v)
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_jtag_dtm_core #(
|
||||
parameter DTMCS_IDLE_HINT = 3'd4,
|
||||
parameter W_ADDR = 8,
|
||||
parameter W_DR_SHIFT = W_ADDR + 32 + 2 // do not modify
|
||||
) (
|
||||
input wire tck,
|
||||
input wire trst_n,
|
||||
|
||||
input wire clk_dmi,
|
||||
input wire rst_n_dmi,
|
||||
|
||||
// DR capture/update (read/write) signals
|
||||
input wire dr_wen,
|
||||
input wire dr_ren,
|
||||
input wire dr_sel_dmi_ndtmcs,
|
||||
input wire [W_DR_SHIFT-1:0] dr_wdata,
|
||||
output wire [W_DR_SHIFT-1:0] dr_rdata,
|
||||
|
||||
// This is synchronous to TCK and asserted for one TCK cycle only
|
||||
output reg dmihardreset_req,
|
||||
|
||||
// Debug Module Interface (APB)
|
||||
output wire dmi_psel,
|
||||
output wire dmi_penable,
|
||||
output wire dmi_pwrite,
|
||||
output wire [W_ADDR-1:0] dmi_paddr,
|
||||
output wire [31:0] dmi_pwdata,
|
||||
input wire [31:0] dmi_prdata,
|
||||
input wire dmi_pready,
|
||||
input wire dmi_pslverr
|
||||
);
|
||||
|
||||
wire write_dmi = dr_wen && dr_sel_dmi_ndtmcs;
|
||||
wire write_dtmcs = dr_wen && !dr_sel_dmi_ndtmcs;
|
||||
wire read_dmi = dr_ren && dr_sel_dmi_ndtmcs;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DMI bus adapter
|
||||
|
||||
reg [1:0] dmi_cmderr;
|
||||
reg dmi_busy;
|
||||
|
||||
// DTM-domain bus, connected to a matching DM-domain bus via an APB crossing:
|
||||
wire dtm_psel;
|
||||
wire dtm_penable;
|
||||
wire dtm_pwrite;
|
||||
wire [W_ADDR-1:0] dtm_paddr;
|
||||
wire [31:0] dtm_pwdata;
|
||||
wire [31:0] dtm_prdata;
|
||||
wire dtm_pready;
|
||||
wire dtm_pslverr;
|
||||
|
||||
// We are relying on some particular features of our APB clock crossing here
|
||||
// to save some registers:
|
||||
//
|
||||
// - The transfer is launched immediately when psel is seen, no need to
|
||||
// actually assert an access phase (as the standard allows the CDC to
|
||||
// assume that access immediately follows setup) and no need to maintain
|
||||
// pwrite/paddr/pwdata valid after the setup phase
|
||||
//
|
||||
// - prdata/pslverr remain valid after the transfer completes, until the next
|
||||
// transfer completes
|
||||
//
|
||||
// These allow us to connect the upstream side of the CDC directly to our DR
|
||||
// shifter without any sample/hold registers in between.
|
||||
|
||||
// psel is only pulsed for one cycle, penable is not asserted.
|
||||
assign dtm_psel = write_dmi &&
|
||||
(dr_wdata[1:0] == 2'd1 || dr_wdata[1:0] == 2'd2) &&
|
||||
!(dmi_busy || dmi_cmderr != 2'd0) && dtm_pready;
|
||||
assign dtm_penable = 1'b0;
|
||||
|
||||
// paddr/pwdata/pwrite are valid momentarily when psel is asserted.
|
||||
assign dtm_paddr = dr_wdata[34 +: W_ADDR];
|
||||
assign dtm_pwrite = dr_wdata[1];
|
||||
assign dtm_pwdata = dr_wdata[2 +: 32];
|
||||
|
||||
always @ (posedge tck or negedge trst_n) begin
|
||||
if (!trst_n) begin
|
||||
dmi_busy <= 1'b0;
|
||||
dmi_cmderr <= 2'd0;
|
||||
end else if (read_dmi) begin
|
||||
// Reading while busy sets the busy sticky error. Note the capture
|
||||
// into shift register should also reflect this update on-the-fly
|
||||
if (dmi_busy && dmi_cmderr == 2'd0)
|
||||
dmi_cmderr <= 2'h3;
|
||||
end else if (write_dtmcs) begin
|
||||
// Writing dtmcs.dmireset = 1 clears a sticky error
|
||||
if (dr_wdata[16])
|
||||
dmi_cmderr <= 2'd0;
|
||||
end else if (write_dmi) begin
|
||||
if (dtm_psel) begin
|
||||
dmi_busy <= 1'b1;
|
||||
end else if (dr_wdata[1:0] != 2'd0) begin
|
||||
// DMI ignored operation, so set sticky busy
|
||||
if (dmi_cmderr == 2'd0)
|
||||
dmi_cmderr <= 2'd3;
|
||||
end
|
||||
end else if (dmi_busy && dtm_pready) begin
|
||||
dmi_busy <= 1'b0;
|
||||
if (dmi_cmderr == 2'd0 && dtm_pslverr)
|
||||
dmi_cmderr <= 2'd2;
|
||||
end
|
||||
end
|
||||
|
||||
// DTM logic is in TCK domain, actual DMI + DM is in processor domain
|
||||
|
||||
hazard3_apb_async_bridge #(
|
||||
.W_ADDR (W_ADDR),
|
||||
.W_DATA (32),
|
||||
.N_SYNC_STAGES (2)
|
||||
) inst_hazard3_apb_async_bridge (
|
||||
.clk_src (tck),
|
||||
.rst_n_src (trst_n),
|
||||
|
||||
.clk_dst (clk_dmi),
|
||||
.rst_n_dst (rst_n_dmi),
|
||||
|
||||
.src_psel (dtm_psel),
|
||||
.src_penable (dtm_penable),
|
||||
.src_pwrite (dtm_pwrite),
|
||||
.src_paddr (dtm_paddr),
|
||||
.src_pwdata (dtm_pwdata),
|
||||
.src_prdata (dtm_prdata),
|
||||
.src_pready (dtm_pready),
|
||||
.src_pslverr (dtm_pslverr),
|
||||
|
||||
.dst_psel (dmi_psel),
|
||||
.dst_penable (dmi_penable),
|
||||
.dst_pwrite (dmi_pwrite),
|
||||
.dst_paddr (dmi_paddr),
|
||||
.dst_pwdata (dmi_pwdata),
|
||||
.dst_prdata (dmi_prdata),
|
||||
.dst_pready (dmi_pready),
|
||||
.dst_pslverr (dmi_pslverr)
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DR read/write
|
||||
|
||||
wire [W_DR_SHIFT-1:0] dtmcs_rdata = {
|
||||
{W_ADDR{1'b0}},
|
||||
19'h0,
|
||||
DTMCS_IDLE_HINT[2:0],
|
||||
dmi_cmderr,
|
||||
W_ADDR[5:0], // abits
|
||||
4'd1 // version
|
||||
};
|
||||
|
||||
wire [W_DR_SHIFT-1:0] dmi_rdata = {
|
||||
{W_ADDR{1'b0}},
|
||||
dtm_prdata,
|
||||
dmi_busy && dmi_cmderr == 2'd0 ? 2'd3 : dmi_cmderr
|
||||
};
|
||||
|
||||
assign dr_rdata = dr_sel_dmi_ndtmcs ? dmi_rdata : dtmcs_rdata;
|
||||
|
||||
always @ (posedge tck or negedge trst_n) begin
|
||||
if (!trst_n) begin
|
||||
dmihardreset_req <= 1'b0;
|
||||
end else begin
|
||||
dmihardreset_req <= write_dtmcs && dr_wdata[17];
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,162 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2025 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Implement a RISC-V JTAG DTM tunnelled through a Xilinx BSCANE2 primitive.
|
||||
//
|
||||
// Xilinx allows up to four custom DRs to be added to the FPGA TAP controller.
|
||||
// A JTAG-DTM only needs two: DTMCS and DMI.
|
||||
//
|
||||
// With the correct config, OpenOCD can treat the FPGA TAP as a JTAG DTM and
|
||||
// access RISC-V debug directly. This allows you to debug internal RISC-V
|
||||
// cores with the same JTAG interface you use to load the FPGA.
|
||||
//
|
||||
// CHAIN_DTMCS and CHAIN_DMI select which JTAG IR values are used to access
|
||||
// these DRs. Values 1 through 4 correspond to Xilinx USER1 through USER4
|
||||
// instructions, which have IR values 0x02, 0x03, 0x22, 0x23.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_xilinx7_jtag_dtm #(
|
||||
parameter SEL_DTMCS = 3,
|
||||
parameter SEL_DMI = 4,
|
||||
parameter DTMCS_IDLE_HINT = 3'd4,
|
||||
parameter W_PADDR = 9,
|
||||
parameter ABITS = W_PADDR - 2 // do not modify
|
||||
) (
|
||||
// This is synchronous to TCK and asserted for one TCK cycle only
|
||||
output wire dmihardreset_req,
|
||||
|
||||
// Bus clock + reset for Debug Module Interface
|
||||
input wire clk_dmi,
|
||||
input wire rst_n_dmi,
|
||||
|
||||
// Debug Module Interface (APB)
|
||||
output wire dmi_psel,
|
||||
output wire dmi_penable,
|
||||
output wire dmi_pwrite,
|
||||
output wire [W_PADDR-1:0] dmi_paddr,
|
||||
output wire [31:0] dmi_pwdata,
|
||||
input wire [31:0] dmi_prdata,
|
||||
input wire dmi_pready,
|
||||
input wire dmi_pslverr
|
||||
);
|
||||
|
||||
// Signals to/from the Xilinx TAP
|
||||
|
||||
wire jtck_unbuf;
|
||||
wire jtck;
|
||||
wire jtdo2;
|
||||
wire jtdo1;
|
||||
wire jtdi;
|
||||
wire jshift;
|
||||
wire jupdate;
|
||||
wire jcapture;
|
||||
wire jrst;
|
||||
wire jrst_n = !jrst;
|
||||
wire jce2;
|
||||
wire jce1;
|
||||
|
||||
BSCANE2 #(
|
||||
.JTAG_CHAIN (SEL_DTMCS) // Value for USER command.
|
||||
) bscan_dtmcs (
|
||||
.CAPTURE (jcapture), // CAPTURE output from TAP controller.
|
||||
.DRCK (/* unused */), // Gated TCK output. When SEL is asserted, DRCK toggles when CAPTURE or SHIFT are asserted.
|
||||
.RESET (jrst), // Reset output for TAP controller.
|
||||
.RUNTEST (/* unused */), // Output asserted when TAP controller is in Run Test/Idle state.
|
||||
.SEL (jce1), // USER instruction active output.
|
||||
.SHIFT (jshift), // SHIFT output from TAP controller.
|
||||
.TCK (jtck_unbuf), // Test Clock output. Fabric connection to TAP Clock pin.
|
||||
.TDI (jtdi), // Test Data Input (TDI) output from TAP controller.
|
||||
.TMS (/* unused */), // Test Mode Select output. Fabric connection to TAP.
|
||||
.UPDATE (jupdate), // UPDATE output from TAP controller
|
||||
.TDO (jtdo1) // Test Data Output (TDO) input for USER function.
|
||||
);
|
||||
|
||||
BSCANE2 #(
|
||||
.JTAG_CHAIN (SEL_DMI)
|
||||
) bscan_dmi (
|
||||
.CAPTURE (/* unused */),
|
||||
.DRCK (/* unused */),
|
||||
.RESET (/* unused */),
|
||||
.RUNTEST (/* unused */),
|
||||
.SEL (jce2),
|
||||
.SHIFT (/* unused */),
|
||||
.TCK (/* unused */),
|
||||
.TDI (/* unused */),
|
||||
.TMS (/* unused */),
|
||||
.UPDATE (/* unused */),
|
||||
.TDO (jtdo2)
|
||||
);
|
||||
|
||||
BUFG bufg_jtck (
|
||||
.I (jtck_unbuf),
|
||||
.O (jtck)
|
||||
);
|
||||
|
||||
localparam W_DR_SHIFT = ABITS + 32 + 2;
|
||||
|
||||
wire core_dr_wen = jupdate;
|
||||
wire core_dr_ren = jcapture;
|
||||
wire core_dr_sel_dmi_ndtmcs = !jce1;
|
||||
wire dr_shift_en = jshift;
|
||||
wire [W_DR_SHIFT-1:0] core_dr_wdata;
|
||||
wire [W_DR_SHIFT-1:0] core_dr_rdata;
|
||||
|
||||
reg [W_DR_SHIFT-1:0] dr_shift;
|
||||
assign core_dr_wdata = dr_shift;
|
||||
|
||||
always @ (posedge jtck or negedge jrst_n) begin
|
||||
if (!jrst_n) begin
|
||||
dr_shift <= {W_DR_SHIFT{1'b0}};
|
||||
end else if (core_dr_ren) begin
|
||||
dr_shift <= core_dr_rdata;
|
||||
end else if (dr_shift_en) begin
|
||||
dr_shift <= {jtdi, dr_shift[W_DR_SHIFT-1:1]};
|
||||
if (!core_dr_sel_dmi_ndtmcs)
|
||||
dr_shift[31] <= jtdi;
|
||||
end
|
||||
end
|
||||
|
||||
// We have only a single shifter for the two DRs, so these are tied together:
|
||||
assign jtdo1 = dr_shift[0];
|
||||
assign jtdo2 = dr_shift[0];
|
||||
|
||||
// The actual DTM is in here:
|
||||
|
||||
hazard3_jtag_dtm_core #(
|
||||
.DTMCS_IDLE_HINT (DTMCS_IDLE_HINT),
|
||||
.W_ADDR (ABITS)
|
||||
) inst_hazard3_jtag_dtm_core (
|
||||
.tck (jtck),
|
||||
.trst_n (jrst_n),
|
||||
|
||||
.clk_dmi (clk_dmi),
|
||||
.rst_n_dmi (rst_n_dmi),
|
||||
|
||||
.dr_wen (core_dr_wen),
|
||||
.dr_ren (core_dr_ren),
|
||||
.dr_sel_dmi_ndtmcs (core_dr_sel_dmi_ndtmcs),
|
||||
.dr_wdata (core_dr_wdata),
|
||||
.dr_rdata (core_dr_rdata),
|
||||
|
||||
.dmihardreset_req (dmihardreset_req),
|
||||
|
||||
.dmi_psel (dmi_psel),
|
||||
.dmi_penable (dmi_penable),
|
||||
.dmi_pwrite (dmi_pwrite),
|
||||
.dmi_paddr (dmi_paddr[W_PADDR-1:2]),
|
||||
.dmi_pwdata (dmi_pwdata),
|
||||
.dmi_prdata (dmi_prdata),
|
||||
.dmi_pready (dmi_pready),
|
||||
.dmi_pslverr (dmi_pslverr)
|
||||
);
|
||||
|
||||
assign dmi_paddr[1:0] = 2'b00;
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
file hazard3_core.v
|
||||
file hazard3_cpu_1port.v
|
||||
file hazard3_cpu_2port.v
|
||||
file arith/hazard3_alu.v
|
||||
file arith/hazard3_branchcmp.v
|
||||
file arith/hazard3_mul_fast.v
|
||||
file arith/hazard3_muldiv_seq.v
|
||||
file arith/hazard3_onehot_encode.v
|
||||
file arith/hazard3_onehot_priority.v
|
||||
file arith/hazard3_onehot_priority_dynamic.v
|
||||
file arith/hazard3_priority_encode.v
|
||||
file arith/hazard3_shift_barrel.v
|
||||
file hazard3_csr.v
|
||||
file hazard3_decode.v
|
||||
file hazard3_frontend.v
|
||||
file hazard3_instr_decompress.v
|
||||
file hazard3_irq_ctrl.v
|
||||
file hazard3_pmp.v
|
||||
file hazard3_power_ctrl.v
|
||||
file hazard3_regfile_1w2r.v
|
||||
file hazard3_triggers.v
|
||||
include .
|
||||
Vendored
+259
@@ -0,0 +1,259 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Hazard3 CPU configuration parameters
|
||||
|
||||
// To configure Hazard3 you can either edit this file, or set parameters on
|
||||
// your top-level instantiation, it's up to you. These parameters are all
|
||||
// plumbed through Hazard3's internal hierarchy to the appropriate places.
|
||||
|
||||
// If you add a parameter here, you should add a matching line to
|
||||
// hazard3_config_inst.vh to propagate the parameter through module
|
||||
// instantiations.
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Reset state configuration
|
||||
|
||||
// RESET_VECTOR: Address of first instruction executed.
|
||||
parameter RESET_VECTOR = 32'h00000000,
|
||||
|
||||
// MTVEC_INIT: Initial value of trap vector base. Bits clear in MTVEC_WMASK
|
||||
// will never change from this initial value. Bits set in MTVEC_WMASK can be
|
||||
// written/set/cleared as normal.
|
||||
//
|
||||
// Note that mtvec bits 1:0 do not affect the trap base (as per RISC-V spec).
|
||||
// Bit 1 is don't care, bit 0 selects the vectoring mode: unvectored if == 0
|
||||
// (all traps go to mtvec), vectored if == 1 (exceptions go to mtvec, IRQs to
|
||||
// mtvec + mcause * 4). This means MTVEC_INIT also sets the initial vectoring
|
||||
// mode.
|
||||
parameter MTVEC_INIT = 32'h00000000,
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Standard RISC-V ISA support
|
||||
|
||||
// EXTENSION_A: Support for atomic read/modify/write instructions
|
||||
parameter EXTENSION_A = 1,
|
||||
|
||||
// EXTENSION_C: Support for compressed (variable-width) instructions
|
||||
parameter EXTENSION_C = 1,
|
||||
|
||||
// EXTENSION_E: Implement the RV32E base extension rather than RV32I. This
|
||||
// reduces the number of integer registers from 31 to 15.
|
||||
parameter EXTENSION_E = 0,
|
||||
|
||||
// EXTENSION_M: Support for hardware multiply/divide/modulo instructions
|
||||
parameter EXTENSION_M = 1,
|
||||
|
||||
// EXTENSION_ZBA: Support for Zba address generation instructions
|
||||
parameter EXTENSION_ZBA = 0,
|
||||
|
||||
// EXTENSION_ZBB: Support for Zbb basic bit manipulation instructions
|
||||
parameter EXTENSION_ZBB = 0,
|
||||
|
||||
// EXTENSION_ZBC: Support for Zbc carry-less multiplication instructions
|
||||
parameter EXTENSION_ZBC = 0,
|
||||
|
||||
// EXTENSION_ZBKB: Support for Zbkb basic bit manipulation for cryptography
|
||||
// Requires: Zbb. (This flag enables instructions in Zbkb which aren't in Zbb.)
|
||||
parameter EXTENSION_ZBKB = 0,
|
||||
|
||||
// EXTENSION_ZBKX: support for Zbkx crossbar permutation instructions
|
||||
parameter EXTENSION_ZBKX = 0,
|
||||
|
||||
// EXTENSION_ZBS: Support for Zbs single-bit manipulation instructions
|
||||
parameter EXTENSION_ZBS = 0,
|
||||
|
||||
// EXTENSION_ZCB: Support for Zcb basic additional compressed instructions
|
||||
// Requires: EXTENSION_C. (Some Zcb instructions also require Zbb or M.)
|
||||
// Note Zca is equivalent to C, as we do not support the F extension.
|
||||
parameter EXTENSION_ZCB = 0,
|
||||
|
||||
// EXTENSION_ZCLSD: Support for Zclsd compressed load/store pair instructions
|
||||
// Requires: EXTENSION_ZILSD, EXTENSION_C.
|
||||
parameter EXTENSION_ZCLSD = 0,
|
||||
|
||||
// EXTENSION_ZCMP: Support for Zcmp push/pop instructions.
|
||||
// Requires: EXTENSION_C.
|
||||
parameter EXTENSION_ZCMP = 0,
|
||||
|
||||
// EXTENSION_ZIFENCEI: Support for the fence.i instruction
|
||||
// Optional, since a plain branch/jump will also flush the prefetch queue.
|
||||
parameter EXTENSION_ZIFENCEI = 0,
|
||||
|
||||
// EXTENSION_ZILSD: Support for Zilsd load/store pair instructions
|
||||
parameter EXTENSION_ZILSD = 0,
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Custom RISC-V extensions
|
||||
|
||||
// EXTENSION_XH3B: Custom bit-extract-multiple instructions for Hazard3
|
||||
parameter EXTENSION_XH3BEXTM = 0,
|
||||
|
||||
// EXTENSION_XH3IRQ: Custom preemptive, prioritised interrupt support. Can be
|
||||
// disabled if an external interrupt controller (e.g. PLIC) is used. If
|
||||
// disabled, and NUM_IRQS > 1, the external interrupts are simply OR'd into
|
||||
// mip.meip.
|
||||
parameter EXTENSION_XH3IRQ = 0,
|
||||
|
||||
// EXTENSION_XH3PMPM: PMPCFGMx CSRs to enforce PMP regions in M-mode without
|
||||
// locking. Unlike ePMP mseccfg.rlb, locked and unlocked regions can coexist
|
||||
parameter EXTENSION_XH3PMPM = 0,
|
||||
|
||||
// EXTENSION_XH3POWER: Custom power management controls for Hazard3
|
||||
parameter EXTENSION_XH3POWER = 0,
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Standard CSR support
|
||||
|
||||
// Note the Zicsr extension is implied by any of CSR_M_MANDATORY, CSR_M_TRAP,
|
||||
// CSR_COUNTER.
|
||||
|
||||
// CSR_M_MANDATORY: Bare minimum CSR support e.g. misa. Spec says must = 1 if
|
||||
// CSRs are present, but I won't tell anyone.
|
||||
parameter CSR_M_MANDATORY = 1,
|
||||
|
||||
// CSR_M_TRAP: Include M-mode trap-handling CSRs, and enable trap support.
|
||||
parameter CSR_M_TRAP = 1,
|
||||
|
||||
// CSR_COUNTER: Include performance counters and Zicntr CSRs
|
||||
parameter CSR_COUNTER = 0,
|
||||
|
||||
// U_MODE: Support the U (user) execution mode. In U mode, the core performs
|
||||
// unprivileged bus accesses, and software's access to CSRs is restricted.
|
||||
// Additionally, if the PMP is included, the core may restrict U-mode
|
||||
// software's access to memory.
|
||||
// Requires: CSR_M_TRAP.
|
||||
parameter U_MODE = 0,
|
||||
|
||||
// PMP_REGIONS: Number of physical memory protection regions, or 0 for no PMP.
|
||||
// PMP is more useful if U mode is supported, but this is not a requirement.
|
||||
parameter PMP_REGIONS = 0,
|
||||
|
||||
// PMP_GRAIN: This is the "G" parameter in the privileged spec. Minimum PMP
|
||||
// region size is 1 << (G + 2) bytes. If G > 0, PMCFG.A can not be set to
|
||||
// NA4 (will get set to OFF instead). If G > 1, the G - 1 LSBs of pmpaddr are
|
||||
// read-only-0 when PMPCFG.A is OFF, and read-only-1 when PMPCFG.A is NAPOT.
|
||||
parameter PMP_GRAIN = 0,
|
||||
|
||||
// PMP_MATCH_NAPOT: Enable PMP support for the NAPOT (naturally-aligned
|
||||
// power-of-two) and NA4 (naturally-aligned four-byte) matching modes. When
|
||||
// disabled, attempting to select these modes will set the PMP region to OFF.
|
||||
parameter PMP_MATCH_NAPOT = 1,
|
||||
|
||||
// PMP_MATCH_TOR: Enable PMP support for the TOR (top-of-range) matching mode.
|
||||
// When disabled, attempting to select this mode will set the region to OFF.
|
||||
parameter PMP_MATCH_TOR = 0,
|
||||
|
||||
// PMPADDR_HARDWIRED: If a bit is 1, the corresponding region's pmpaddr and
|
||||
// pmpcfg registers are read-only. PMP_GRAIN is ignored on hardwired regions.
|
||||
// It's recommended to make hardwired regions the highest-numbered, so they
|
||||
// can be overridden by lower-numbered regions.
|
||||
parameter PMP_HARDWIRED = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){1'b0}},
|
||||
|
||||
// PMPADDR_HARDWIRED_ADDR: Values of pmpaddr registers whose PMP_HARDWIRED
|
||||
// bits are set to 1. Non-hardwired regions reset to all-zeroes.
|
||||
parameter PMP_HARDWIRED_ADDR = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){32'h0}},
|
||||
|
||||
// PMPCFG_RESET_VAL: Values of pmpcfg registers whose PMP_HARDWIRED bits are
|
||||
// set to 1. Non-hardwired regions reset to all zeroes.
|
||||
parameter PMP_HARDWIRED_CFG = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){8'h00}},
|
||||
|
||||
// DEBUG_SUPPORT: Support for run/halt and instruction injection from an
|
||||
// external Debug Module, support for Debug Mode, and Debug Mode CSRs.
|
||||
// Requires: CSR_M_MANDATORY, CSR_M_TRAP.
|
||||
parameter DEBUG_SUPPORT = 0,
|
||||
|
||||
// BREAKPOINT_TRIGGERS: Number of triggers which support type=2 execute=1
|
||||
// (but not store/load=1, i.e. not a watchpoint). Requires: DEBUG_SUPPORT
|
||||
parameter BREAKPOINT_TRIGGERS = 0,
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// External interrupt support
|
||||
|
||||
// NUM_IRQS: Number of external IRQs. Minimum 1, maximum 512. Note that if
|
||||
// EXTENSION_XH3IRQ (Hazard3 interrupt controller) is disabled then multiple
|
||||
// external interrupts are simply OR'd into mip.meip.
|
||||
parameter NUM_IRQS = 1,
|
||||
|
||||
// IRQ_PRIORITY_BITS: Number of priority bits implemented for each interrupt
|
||||
// in meipra, if EXTENSION_XH3IRQ is enabled. The number of distinct levels
|
||||
// is (1 << IRQ_PRIORITY_BITS). Minimum 0, max 4. Note that multiple priority
|
||||
// levels with a large number of IRQs will have a severe effect on timing.
|
||||
parameter IRQ_PRIORITY_BITS = 0,
|
||||
|
||||
// IRQ_INPUT_BYPASS: disable the input registers on the external interrupts,
|
||||
// to reduce latency by one cycle. Can be applied on an IRQ-by-IRQ basis.
|
||||
// Ignored if EXTENSION_XH3IRQ is disabled.
|
||||
parameter IRQ_INPUT_BYPASS = {(NUM_IRQS > 0 ? NUM_IRQS : 1){1'b0}},
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ID registers
|
||||
|
||||
// JEDEC JEP106-compliant vendor ID, can be left at 0 if "not implemented or
|
||||
// [...] this is a non-commercial implementation" (RISC-V spec).
|
||||
// 31:7 is continuation code count, 6:0 is ID. Parity bit is not stored.
|
||||
parameter MVENDORID_VAL = 32'h0,
|
||||
|
||||
// Pointer to configuration structure blob, or all-zeroes. Must be at least
|
||||
// 4-byte-aligned.
|
||||
parameter MCONFIGPTR_VAL = 32'h0,
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Performance/size options
|
||||
|
||||
// REDUCED_BYPASS: Remove all forwarding paths except X->X (so back-to-back
|
||||
// ALU ops can still run at 1 CPI), to save area.
|
||||
parameter REDUCED_BYPASS = 0,
|
||||
|
||||
// MULDIV_UNROLL: Bits per clock for multiply/divide circuit, if present. Must
|
||||
// be a power of 2.
|
||||
parameter MULDIV_UNROLL = 1,
|
||||
|
||||
// MUL_FAST: Use single-cycle multiply circuit for MUL instructions, retiring
|
||||
// to stage 3. The sequential multiply/divide circuit is still used for MULH*
|
||||
parameter MUL_FAST = 0,
|
||||
|
||||
// MUL_FASTER: Retire fast multiply results to stage 2 instead of stage 3.
|
||||
// Throughput is the same, but latency is reduced from 2 cycles to 1 cycle.
|
||||
// Requires: MUL_FAST.
|
||||
parameter MUL_FASTER = 0,
|
||||
|
||||
// MULH_FAST: extend the fast multiply circuit to also cover MULH*, and remove
|
||||
// the multiply functionality from the sequential multiply/divide circuit.
|
||||
// Requires: MUL_FAST
|
||||
parameter MULH_FAST = 0,
|
||||
|
||||
// FAST_BRANCHCMP: Instantiate a separate comparator (eq/lt/ltu) for branch
|
||||
// comparisons, rather than using the ALU. Improves fetch address delay,
|
||||
// especially if Zba extension is enabled. Disabling may save area.
|
||||
parameter FAST_BRANCHCMP = 1,
|
||||
|
||||
// RESET_REGFILE: whether to support reset of the general purpose registers.
|
||||
// There are around 1k bits in the register file, so the reset can be
|
||||
// disabled e.g. to permit block-RAM inference on FPGA.
|
||||
parameter RESET_REGFILE = 0,
|
||||
|
||||
// BRANCH_PREDICTOR: enable branch prediction. The branch predictor consists
|
||||
// of a single BTB entry which is allocated on a taken backward branch, and
|
||||
// cleared on a mispredicted nontaken branch, a fence.i or a trap. Successful
|
||||
// prediction eliminates the 1-cyle fetch bubble on a taken branch, usually
|
||||
// making tight loops faster.
|
||||
parameter BRANCH_PREDICTOR = 0,
|
||||
|
||||
// MTVEC_WMASK: Mask of which bits in mtvec are writable. Full writability is
|
||||
// recommended, because a common idiom in setup code is to set mtvec just
|
||||
// past code that may trap, as a hardware "try {...} catch" block.
|
||||
//
|
||||
// - The vectoring mode can be made fixed by clearing the LSB of MTVEC_WMASK
|
||||
//
|
||||
// - In vectored mode, the vector table must be aligned to its size, rounded
|
||||
// up to a power of two.
|
||||
parameter MTVEC_WMASK = 32'hfffffffd,
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Port size parameters (do not modify)
|
||||
|
||||
parameter W_ADDR = 32, // Do not modify
|
||||
parameter W_DATA = 32 // Do not modify
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Pass-through of parameters defined in hazard3_config.vh, so that these can
|
||||
// be set at instantiation rather than editing the config file, and will flow
|
||||
// correctly down through the hierarchy.
|
||||
|
||||
.RESET_VECTOR (RESET_VECTOR),
|
||||
.MTVEC_INIT (MTVEC_INIT),
|
||||
.EXTENSION_A (EXTENSION_A),
|
||||
.EXTENSION_C (EXTENSION_C),
|
||||
.EXTENSION_E (EXTENSION_E),
|
||||
.EXTENSION_M (EXTENSION_M),
|
||||
.EXTENSION_ZBA (EXTENSION_ZBA),
|
||||
.EXTENSION_ZBB (EXTENSION_ZBB),
|
||||
.EXTENSION_ZBC (EXTENSION_ZBC),
|
||||
.EXTENSION_ZBKB (EXTENSION_ZBKB),
|
||||
.EXTENSION_ZBKX (EXTENSION_ZBKX),
|
||||
.EXTENSION_ZBS (EXTENSION_ZBS),
|
||||
.EXTENSION_ZCB (EXTENSION_ZCB),
|
||||
.EXTENSION_ZCLSD (EXTENSION_ZCLSD),
|
||||
.EXTENSION_ZCMP (EXTENSION_ZCMP),
|
||||
.EXTENSION_ZIFENCEI (EXTENSION_ZIFENCEI),
|
||||
.EXTENSION_ZILSD (EXTENSION_ZILSD),
|
||||
.EXTENSION_XH3BEXTM (EXTENSION_XH3BEXTM),
|
||||
.EXTENSION_XH3IRQ (EXTENSION_XH3IRQ),
|
||||
.EXTENSION_XH3PMPM (EXTENSION_XH3PMPM),
|
||||
.EXTENSION_XH3POWER (EXTENSION_XH3POWER),
|
||||
.CSR_M_MANDATORY (CSR_M_MANDATORY),
|
||||
.CSR_M_TRAP (CSR_M_TRAP),
|
||||
.CSR_COUNTER (CSR_COUNTER),
|
||||
.U_MODE (U_MODE),
|
||||
.PMP_REGIONS (PMP_REGIONS),
|
||||
.PMP_GRAIN (PMP_GRAIN),
|
||||
.PMP_MATCH_NAPOT (PMP_MATCH_NAPOT),
|
||||
.PMP_MATCH_TOR (PMP_MATCH_TOR),
|
||||
.PMP_HARDWIRED (PMP_HARDWIRED),
|
||||
.PMP_HARDWIRED_ADDR (PMP_HARDWIRED_ADDR),
|
||||
.PMP_HARDWIRED_CFG (PMP_HARDWIRED_CFG),
|
||||
.DEBUG_SUPPORT (DEBUG_SUPPORT),
|
||||
.BREAKPOINT_TRIGGERS (BREAKPOINT_TRIGGERS),
|
||||
.NUM_IRQS (NUM_IRQS),
|
||||
.IRQ_PRIORITY_BITS (IRQ_PRIORITY_BITS),
|
||||
.IRQ_INPUT_BYPASS (IRQ_INPUT_BYPASS),
|
||||
.MVENDORID_VAL (MVENDORID_VAL),
|
||||
.MCONFIGPTR_VAL (MCONFIGPTR_VAL),
|
||||
.REDUCED_BYPASS (REDUCED_BYPASS),
|
||||
.MULDIV_UNROLL (MULDIV_UNROLL),
|
||||
.MUL_FAST (MUL_FAST),
|
||||
.MUL_FASTER (MUL_FASTER),
|
||||
.MULH_FAST (MULH_FAST),
|
||||
.FAST_BRANCHCMP (FAST_BRANCHCMP),
|
||||
.BRANCH_PREDICTOR (BRANCH_PREDICTOR),
|
||||
.MTVEC_WMASK (MTVEC_WMASK),
|
||||
.RESET_REGFILE (RESET_REGFILE),
|
||||
.W_ADDR (W_ADDR),
|
||||
.W_DATA (W_DATA)
|
||||
Vendored
+1590
File diff suppressed because it is too large
Load Diff
+343
@@ -0,0 +1,343 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Single-ported top level file for Hazard3 CPU. This file instantiates the
|
||||
// Hazard3 core, and arbitrates its instruction fetch and load/store signals
|
||||
// down to a single AHB5 master port.
|
||||
|
||||
`ifdef HAZARD3_RVFI_STANDALONE
|
||||
`include "hazard3_rvfi_standalone_defs.vh"
|
||||
`endif
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_cpu_1port #(
|
||||
`include "hazard3_config.vh"
|
||||
) (
|
||||
// Global signals
|
||||
input wire clk,
|
||||
input wire clk_always_on,
|
||||
input wire rst_n,
|
||||
|
||||
`ifdef RISCV_FORMAL
|
||||
`RVFI_OUTPUTS ,
|
||||
`endif
|
||||
|
||||
// Power control signals
|
||||
output wire pwrup_req,
|
||||
input wire pwrup_ack,
|
||||
output wire clk_en,
|
||||
output wire unblock_out,
|
||||
input wire unblock_in,
|
||||
|
||||
// AHB5 Master port
|
||||
output reg [W_ADDR-1:0] haddr,
|
||||
output reg hwrite,
|
||||
output reg [1:0] htrans,
|
||||
output reg [2:0] hsize,
|
||||
output wire [2:0] hburst,
|
||||
output reg [3:0] hprot,
|
||||
output wire hmastlock,
|
||||
output reg [7:0] hmaster,
|
||||
output reg hexcl,
|
||||
input wire hready,
|
||||
input wire hresp,
|
||||
input wire hexokay,
|
||||
output wire [W_DATA-1:0] hwdata,
|
||||
input wire [W_DATA-1:0] hrdata,
|
||||
|
||||
// Memory ordering signals
|
||||
output wire fence_i_vld,
|
||||
output wire fence_d_vld,
|
||||
input wire fence_rdy,
|
||||
|
||||
// Debugger run/halt control
|
||||
input wire dbg_req_halt,
|
||||
input wire dbg_req_halt_on_reset,
|
||||
input wire dbg_req_resume,
|
||||
output wire dbg_halted,
|
||||
output wire dbg_running,
|
||||
// Debugger access to data0 CSR
|
||||
input wire [W_DATA-1:0] dbg_data0_rdata,
|
||||
output wire [W_DATA-1:0] dbg_data0_wdata,
|
||||
output wire dbg_data0_wen,
|
||||
// Debugger instruction injection
|
||||
input wire [W_DATA-1:0] dbg_instr_data,
|
||||
input wire dbg_instr_data_vld,
|
||||
output wire dbg_instr_data_rdy,
|
||||
output wire dbg_instr_caught_exception,
|
||||
output wire dbg_instr_caught_ebreak,
|
||||
|
||||
// Optional debug system bus access patch-through
|
||||
input wire [W_ADDR-1:0] dbg_sbus_addr,
|
||||
input wire dbg_sbus_write,
|
||||
input wire [1:0] dbg_sbus_size,
|
||||
input wire dbg_sbus_vld,
|
||||
output wire dbg_sbus_rdy,
|
||||
output wire dbg_sbus_err,
|
||||
input wire [W_DATA-1:0] dbg_sbus_wdata,
|
||||
output wire [W_DATA-1:0] dbg_sbus_rdata,
|
||||
|
||||
// Identification CSR values
|
||||
input wire [W_DATA-1:0] mhartid_val,
|
||||
input wire [3:0] eco_version,
|
||||
|
||||
// Level-sensitive interrupt sources
|
||||
input wire [NUM_IRQS-1:0] irq, // -> mip.meip
|
||||
input wire soft_irq, // -> mip.msip
|
||||
input wire timer_irq // -> mip.mtip
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Processor core
|
||||
|
||||
// Instruction fetch signals
|
||||
wire core_aph_req_i;
|
||||
wire core_aph_panic_i;
|
||||
wire core_aph_ready_i;
|
||||
wire core_dph_ready_i;
|
||||
wire core_dph_err_i;
|
||||
|
||||
wire [W_ADDR-1:0] core_haddr_i;
|
||||
wire [2:0] core_hsize_i;
|
||||
wire core_priv_i;
|
||||
wire [W_DATA-1:0] core_rdata_i;
|
||||
|
||||
|
||||
// Load/store signals
|
||||
wire core_aph_req_d;
|
||||
wire core_aph_excl_d;
|
||||
wire core_aph_ready_d;
|
||||
wire core_dph_ready_d;
|
||||
wire core_dph_err_d;
|
||||
wire core_dph_exokay_d;
|
||||
|
||||
wire [W_ADDR-1:0] core_haddr_d;
|
||||
wire [2:0] core_hsize_d;
|
||||
wire core_priv_d;
|
||||
wire core_hwrite_d;
|
||||
wire [W_DATA-1:0] core_wdata_d;
|
||||
wire [W_DATA-1:0] core_rdata_d;
|
||||
|
||||
|
||||
hazard3_core #(
|
||||
`include "hazard3_config_inst.vh"
|
||||
) core (
|
||||
.clk (clk),
|
||||
.clk_always_on (clk_always_on),
|
||||
.rst_n (rst_n),
|
||||
|
||||
.pwrup_req (pwrup_req),
|
||||
.pwrup_ack (pwrup_ack),
|
||||
.clk_en (clk_en),
|
||||
.unblock_out (unblock_out),
|
||||
.unblock_in (unblock_in),
|
||||
|
||||
`ifdef RISCV_FORMAL
|
||||
`RVFI_CONN ,
|
||||
`endif
|
||||
|
||||
.bus_aph_req_i (core_aph_req_i),
|
||||
.bus_aph_panic_i (core_aph_panic_i),
|
||||
.bus_aph_ready_i (core_aph_ready_i),
|
||||
.bus_dph_ready_i (core_dph_ready_i),
|
||||
.bus_dph_err_i (core_dph_err_i),
|
||||
.bus_haddr_i (core_haddr_i),
|
||||
.bus_hsize_i (core_hsize_i),
|
||||
.bus_priv_i (core_priv_i),
|
||||
.bus_rdata_i (core_rdata_i),
|
||||
|
||||
.bus_aph_req_d (core_aph_req_d),
|
||||
.bus_aph_excl_d (core_aph_excl_d),
|
||||
.bus_aph_ready_d (core_aph_ready_d),
|
||||
.bus_dph_ready_d (core_dph_ready_d),
|
||||
.bus_dph_err_d (core_dph_err_d),
|
||||
.bus_dph_exokay_d (core_dph_exokay_d),
|
||||
.bus_haddr_d (core_haddr_d),
|
||||
.bus_hsize_d (core_hsize_d),
|
||||
.bus_priv_d (core_priv_d),
|
||||
.bus_hwrite_d (core_hwrite_d),
|
||||
.bus_wdata_d (core_wdata_d),
|
||||
.bus_rdata_d (core_rdata_d),
|
||||
|
||||
.fence_i_vld (fence_i_vld),
|
||||
.fence_d_vld (fence_d_vld),
|
||||
.fence_rdy (fence_rdy),
|
||||
|
||||
.dbg_req_halt (dbg_req_halt),
|
||||
.dbg_req_halt_on_reset (dbg_req_halt_on_reset),
|
||||
.dbg_req_resume (dbg_req_resume),
|
||||
.dbg_halted (dbg_halted),
|
||||
.dbg_running (dbg_running),
|
||||
.dbg_data0_rdata (dbg_data0_rdata),
|
||||
.dbg_data0_wdata (dbg_data0_wdata),
|
||||
.dbg_data0_wen (dbg_data0_wen),
|
||||
.dbg_instr_data (dbg_instr_data),
|
||||
.dbg_instr_data_vld (dbg_instr_data_vld),
|
||||
.dbg_instr_data_rdy (dbg_instr_data_rdy),
|
||||
.dbg_instr_caught_exception (dbg_instr_caught_exception),
|
||||
.dbg_instr_caught_ebreak (dbg_instr_caught_ebreak),
|
||||
|
||||
.mhartid_val (mhartid_val),
|
||||
.eco_version (eco_version),
|
||||
|
||||
.irq (irq),
|
||||
.soft_irq (soft_irq),
|
||||
.timer_irq (timer_irq)
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Arbitration state machine
|
||||
|
||||
wire bus_gnt_i;
|
||||
wire bus_gnt_d;
|
||||
wire bus_gnt_s;
|
||||
|
||||
reg bus_hold_aph;
|
||||
reg [2:0] bus_gnt_ids_prev;
|
||||
|
||||
// Note use of clk_always_on: SBA may use this arbiter to access the bus
|
||||
// whilst the core is asleep.
|
||||
|
||||
always @ (posedge clk_always_on or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
bus_hold_aph <= 1'b0;
|
||||
bus_gnt_ids_prev <= 3'h0;
|
||||
end else begin
|
||||
bus_hold_aph <= htrans[1] && !hready && !hresp;
|
||||
bus_gnt_ids_prev <= {bus_gnt_i, bus_gnt_d, bus_gnt_s};
|
||||
end
|
||||
end
|
||||
|
||||
// Debug SBA access is lower priority than load/store, but higher than
|
||||
// instruction fetch. This isn't ideal, but in a tight loop the core may be
|
||||
// performing an instruction fetch or load/store on every single cycle, and
|
||||
// this is a simple way to guarantee eventual success of debugger accesses. A
|
||||
// more complex way would be to add a "panic timer" to boost a stalled sbus
|
||||
// access over an instruction fetch.
|
||||
|
||||
// Note that, often, the sbus will be disconnected: it doesn't provide any
|
||||
// increase in debugger bus throughput compared with the program buffer and
|
||||
// autoexec. It's useful for "minimally intrusive" debug bus access(i.e. less
|
||||
// intrusive than halting the core and resuming it) e.g. for Segger RTT.
|
||||
|
||||
reg bus_active_dph_s;
|
||||
|
||||
assign {bus_gnt_i, bus_gnt_d, bus_gnt_s} =
|
||||
bus_hold_aph ? bus_gnt_ids_prev :
|
||||
core_aph_panic_i ? 3'b100 :
|
||||
core_aph_req_d ? 3'b010 :
|
||||
dbg_sbus_vld && !bus_active_dph_s ? 3'b001 :
|
||||
core_aph_req_i ? 3'b100 :
|
||||
3'b000 ;
|
||||
reg bus_active_dph_i;
|
||||
reg bus_active_dph_d;
|
||||
|
||||
always @ (posedge clk_always_on or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
bus_active_dph_i <= 1'b0;
|
||||
bus_active_dph_d <= 1'b0;
|
||||
bus_active_dph_s <= 1'b0;
|
||||
end else if (hready) begin
|
||||
bus_active_dph_i <= bus_gnt_i;
|
||||
bus_active_dph_d <= bus_gnt_d;
|
||||
bus_active_dph_s <= bus_gnt_s;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Address phase request muxing
|
||||
|
||||
localparam HTRANS_IDLE = 2'b00;
|
||||
localparam HTRANS_NSEQ = 2'b10;
|
||||
|
||||
wire [3:0] hprot_data = {
|
||||
2'b00, // Noncacheable/nonbufferable
|
||||
core_priv_d, // Privileged or Normal as per core state
|
||||
1'b1 // Data access
|
||||
};
|
||||
|
||||
wire [3:0] hprot_instr = {
|
||||
2'b00, // Noncacheable/nonbufferable
|
||||
core_priv_i, // Privileged or Normal as per core state
|
||||
1'b0 // Instruction access
|
||||
};
|
||||
|
||||
wire [3:0] hprot_sbus = {
|
||||
2'b00, // Noncacheable/nonbufferable
|
||||
1'b1, // Always privileged
|
||||
1'b1 // Data access
|
||||
};
|
||||
|
||||
assign hburst = 3'b000; // HBURST_SINGLE
|
||||
assign hmastlock = 1'b0;
|
||||
|
||||
always @ (*) begin
|
||||
if (bus_gnt_s) begin
|
||||
htrans = HTRANS_NSEQ;
|
||||
hexcl = 1'b0;
|
||||
haddr = dbg_sbus_addr;
|
||||
hsize = {1'b0, dbg_sbus_size};
|
||||
hwrite = dbg_sbus_write;
|
||||
hprot = hprot_sbus;
|
||||
hmaster = 8'h01;
|
||||
end else if (bus_gnt_d) begin
|
||||
htrans = HTRANS_NSEQ;
|
||||
hexcl = core_aph_excl_d;
|
||||
haddr = core_haddr_d;
|
||||
hsize = core_hsize_d;
|
||||
hwrite = core_hwrite_d;
|
||||
hprot = hprot_data;
|
||||
hmaster = 8'h00;
|
||||
end else if (bus_gnt_i) begin
|
||||
htrans = HTRANS_NSEQ;
|
||||
hexcl = 1'b0;
|
||||
haddr = core_haddr_i;
|
||||
hsize = core_hsize_i;
|
||||
hwrite = 1'b0;
|
||||
hprot = hprot_instr;
|
||||
hmaster = 8'h00;
|
||||
end else begin
|
||||
htrans = HTRANS_IDLE;
|
||||
hexcl = 1'b0;
|
||||
haddr = {W_ADDR{1'b0}};
|
||||
hsize = 3'h0;
|
||||
hwrite = 1'b0;
|
||||
hprot = 4'h0;
|
||||
hmaster = 8'h00;
|
||||
end
|
||||
end
|
||||
|
||||
assign hwdata = bus_active_dph_s ? dbg_sbus_wdata : core_wdata_d;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Response routing
|
||||
|
||||
// Data buses directly connected
|
||||
assign core_rdata_d = hrdata;
|
||||
assign core_rdata_i = hrdata;
|
||||
assign dbg_sbus_rdata = hrdata;
|
||||
|
||||
// Handhshake based on grant and bus stall
|
||||
assign core_aph_ready_i = hready && bus_gnt_i;
|
||||
assign core_dph_ready_i = bus_active_dph_i && hready;
|
||||
assign core_dph_err_i = bus_active_dph_i && hresp;
|
||||
|
||||
// D-side errors are reported even when not ready, so that the core can make
|
||||
// use of the two-phase error response to cleanly squash a second load/store
|
||||
// chasing the faulting one down the pipeline.
|
||||
assign core_aph_ready_d = hready && bus_gnt_d;
|
||||
assign core_dph_ready_d = bus_active_dph_d && hready;
|
||||
assign core_dph_err_d = bus_active_dph_d && hresp;
|
||||
assign core_dph_exokay_d = bus_active_dph_d && hexokay;
|
||||
|
||||
assign dbg_sbus_err = bus_active_dph_s && hresp;
|
||||
assign dbg_sbus_rdy = bus_active_dph_s && hready;
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Dual-ported top level file for Hazard3 CPU. This file instantiates the
|
||||
// Hazard3 core, and interfaces its instruction fetch and load/store signals
|
||||
// to a pair of AHB5 master ports.
|
||||
|
||||
`ifdef HAZARD3_RVFI_STANDALONE
|
||||
`include "hazard3_rvfi_standalone_defs.vh"
|
||||
`endif
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_cpu_2port #(
|
||||
`include "hazard3_config.vh"
|
||||
) (
|
||||
// Global signals
|
||||
input wire clk,
|
||||
input wire clk_always_on,
|
||||
input wire rst_n,
|
||||
|
||||
// Power control signals
|
||||
output wire pwrup_req,
|
||||
input wire pwrup_ack,
|
||||
output wire clk_en,
|
||||
output wire unblock_out,
|
||||
input wire unblock_in,
|
||||
|
||||
`ifdef RISCV_FORMAL
|
||||
`RVFI_OUTPUTS ,
|
||||
`endif
|
||||
|
||||
// Instruction fetch port
|
||||
output wire [W_ADDR-1:0] i_haddr,
|
||||
output wire i_hwrite,
|
||||
output wire [1:0] i_htrans,
|
||||
output wire [2:0] i_hsize,
|
||||
output wire [2:0] i_hburst,
|
||||
output wire [3:0] i_hprot,
|
||||
output wire i_hmastlock,
|
||||
output wire [7:0] i_hmaster,
|
||||
input wire i_hready,
|
||||
input wire i_hresp,
|
||||
output wire [W_DATA-1:0] i_hwdata,
|
||||
input wire [W_DATA-1:0] i_hrdata,
|
||||
|
||||
// Load/store port
|
||||
output wire [W_ADDR-1:0] d_haddr,
|
||||
output wire d_hwrite,
|
||||
output wire [1:0] d_htrans,
|
||||
output wire [2:0] d_hsize,
|
||||
output wire [2:0] d_hburst,
|
||||
output wire [3:0] d_hprot,
|
||||
output wire d_hmastlock,
|
||||
output wire [7:0] d_hmaster,
|
||||
output wire d_hexcl,
|
||||
input wire d_hready,
|
||||
input wire d_hresp,
|
||||
input wire d_hexokay,
|
||||
output wire [W_DATA-1:0] d_hwdata,
|
||||
input wire [W_DATA-1:0] d_hrdata,
|
||||
|
||||
// Memory ordering signals
|
||||
output wire fence_i_vld,
|
||||
output wire fence_d_vld,
|
||||
input wire fence_rdy,
|
||||
|
||||
// Debugger run/halt control
|
||||
input wire dbg_req_halt,
|
||||
input wire dbg_req_halt_on_reset,
|
||||
input wire dbg_req_resume,
|
||||
output wire dbg_halted,
|
||||
output wire dbg_running,
|
||||
// Debugger access to data0 CSR
|
||||
input wire [W_DATA-1:0] dbg_data0_rdata,
|
||||
output wire [W_DATA-1:0] dbg_data0_wdata,
|
||||
output wire dbg_data0_wen,
|
||||
// Debugger instruction injection
|
||||
input wire [W_DATA-1:0] dbg_instr_data,
|
||||
input wire dbg_instr_data_vld,
|
||||
output wire dbg_instr_data_rdy,
|
||||
output wire dbg_instr_caught_exception,
|
||||
output wire dbg_instr_caught_ebreak,
|
||||
// Optional debug system bus access patch-through
|
||||
input wire [W_ADDR-1:0] dbg_sbus_addr,
|
||||
input wire dbg_sbus_write,
|
||||
input wire [1:0] dbg_sbus_size,
|
||||
input wire dbg_sbus_vld,
|
||||
output wire dbg_sbus_rdy,
|
||||
output wire dbg_sbus_err,
|
||||
input wire [W_DATA-1:0] dbg_sbus_wdata,
|
||||
output wire [W_DATA-1:0] dbg_sbus_rdata,
|
||||
|
||||
// Identification CSR values
|
||||
input wire [W_DATA-1:0] mhartid_val,
|
||||
input wire [3:0] eco_version,
|
||||
|
||||
// Level-sensitive interrupt sources
|
||||
input wire [NUM_IRQS-1:0] irq, // -> mip.meip
|
||||
input wire soft_irq, // -> mip.msip
|
||||
input wire timer_irq // -> mip.mtip
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Processor core
|
||||
|
||||
// Instruction fetch signals
|
||||
wire core_aph_req_i;
|
||||
wire core_aph_ready_i;
|
||||
wire core_dph_ready_i;
|
||||
wire core_dph_err_i;
|
||||
|
||||
wire [W_ADDR-1:0] core_haddr_i;
|
||||
wire [2:0] core_hsize_i;
|
||||
wire core_priv_i;
|
||||
wire [W_DATA-1:0] core_rdata_i;
|
||||
|
||||
|
||||
// Load/store signals
|
||||
wire core_aph_req_d;
|
||||
wire core_aph_excl_d;
|
||||
wire core_aph_ready_d;
|
||||
wire core_dph_ready_d;
|
||||
wire core_dph_err_d;
|
||||
wire core_dph_exokay_d;
|
||||
|
||||
wire [W_ADDR-1:0] core_haddr_d;
|
||||
wire [2:0] core_hsize_d;
|
||||
wire core_priv_d;
|
||||
wire core_hwrite_d;
|
||||
wire [W_DATA-1:0] core_wdata_d;
|
||||
wire [W_DATA-1:0] core_rdata_d;
|
||||
|
||||
hazard3_core #(
|
||||
`include "hazard3_config_inst.vh"
|
||||
) core (
|
||||
.clk (clk),
|
||||
.clk_always_on (clk_always_on),
|
||||
.rst_n (rst_n),
|
||||
|
||||
.pwrup_req (pwrup_req),
|
||||
.pwrup_ack (pwrup_ack),
|
||||
.clk_en (clk_en),
|
||||
.unblock_out (unblock_out),
|
||||
.unblock_in (unblock_in),
|
||||
|
||||
`ifdef RISCV_FORMAL
|
||||
`RVFI_CONN ,
|
||||
`endif
|
||||
|
||||
.bus_aph_req_i (core_aph_req_i),
|
||||
.bus_aph_panic_i (/* unused for 2port */),
|
||||
.bus_aph_ready_i (core_aph_ready_i),
|
||||
.bus_dph_ready_i (core_dph_ready_i),
|
||||
.bus_dph_err_i (core_dph_err_i),
|
||||
.bus_haddr_i (core_haddr_i),
|
||||
.bus_hsize_i (core_hsize_i),
|
||||
.bus_priv_i (core_priv_i),
|
||||
.bus_rdata_i (core_rdata_i),
|
||||
|
||||
.bus_aph_req_d (core_aph_req_d),
|
||||
.bus_aph_excl_d (core_aph_excl_d),
|
||||
.bus_aph_ready_d (core_aph_ready_d),
|
||||
.bus_dph_ready_d (core_dph_ready_d),
|
||||
.bus_dph_err_d (core_dph_err_d),
|
||||
.bus_dph_exokay_d (core_dph_exokay_d),
|
||||
.bus_haddr_d (core_haddr_d),
|
||||
.bus_hsize_d (core_hsize_d),
|
||||
.bus_priv_d (core_priv_d),
|
||||
.bus_hwrite_d (core_hwrite_d),
|
||||
.bus_wdata_d (core_wdata_d),
|
||||
.bus_rdata_d (core_rdata_d),
|
||||
|
||||
.fence_i_vld (fence_i_vld),
|
||||
.fence_d_vld (fence_d_vld),
|
||||
.fence_rdy (fence_rdy),
|
||||
|
||||
.dbg_req_halt (dbg_req_halt),
|
||||
.dbg_req_halt_on_reset (dbg_req_halt_on_reset),
|
||||
.dbg_req_resume (dbg_req_resume),
|
||||
.dbg_halted (dbg_halted),
|
||||
.dbg_running (dbg_running),
|
||||
.dbg_data0_rdata (dbg_data0_rdata),
|
||||
.dbg_data0_wdata (dbg_data0_wdata),
|
||||
.dbg_data0_wen (dbg_data0_wen),
|
||||
.dbg_instr_data (dbg_instr_data),
|
||||
.dbg_instr_data_vld (dbg_instr_data_vld),
|
||||
.dbg_instr_data_rdy (dbg_instr_data_rdy),
|
||||
.dbg_instr_caught_exception (dbg_instr_caught_exception),
|
||||
.dbg_instr_caught_ebreak (dbg_instr_caught_ebreak),
|
||||
|
||||
.mhartid_val (mhartid_val),
|
||||
.eco_version (eco_version),
|
||||
|
||||
.irq (irq),
|
||||
.soft_irq (soft_irq),
|
||||
.timer_irq (timer_irq)
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Instruction port
|
||||
|
||||
localparam HTRANS_IDLE = 2'b00;
|
||||
localparam HTRANS_NSEQ = 2'b10;
|
||||
|
||||
assign i_haddr = core_haddr_i;
|
||||
assign i_htrans = core_aph_req_i ? HTRANS_NSEQ : HTRANS_IDLE;
|
||||
assign i_hsize = core_hsize_i;
|
||||
|
||||
reg dphase_active_i;
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
dphase_active_i <= 1'b0;
|
||||
end else if (i_hready) begin
|
||||
dphase_active_i <= core_aph_req_i;
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
// Wake->sleep transition must wait for outstanding instruction fetches to
|
||||
// complete, in particular because the arbiter clock will stop
|
||||
always @ (posedge clk) if (!rst_n) assert(clk_en || !(core_aph_req_i || dphase_active_i));
|
||||
`endif
|
||||
|
||||
assign core_aph_ready_i = i_hready && core_aph_req_i;
|
||||
assign core_dph_ready_i = i_hready && dphase_active_i;
|
||||
assign core_dph_err_i = i_hready && dphase_active_i && i_hresp;
|
||||
|
||||
assign core_rdata_i = i_hrdata;
|
||||
|
||||
assign i_hwrite = 1'b0;
|
||||
assign i_hburst = 3'h0;
|
||||
assign i_hmastlock = 1'b0;
|
||||
assign i_hmaster = 8'h00;
|
||||
assign i_hwdata = {W_DATA{1'b0}};
|
||||
|
||||
assign i_hprot = {
|
||||
2'b00, // Noncacheable/nonbufferable
|
||||
core_priv_i, // Privileged or Normal as per core state
|
||||
1'b0 // Instruction access
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Load/store port
|
||||
|
||||
// The debug module has optional System Bus Access support, which can be muxed
|
||||
// into the processor's D port here (or connected to a standalone AHB shim).
|
||||
// This confers absolutely no advantage for debugger bus throughput, but
|
||||
// allows the debugger to access the bus with minimal disturbance to the
|
||||
// processor.
|
||||
|
||||
wire bus_gnt_d;
|
||||
wire bus_gnt_s;
|
||||
|
||||
reg bus_hold_aph;
|
||||
reg [1:0] bus_gnt_ds_prev;
|
||||
reg bus_active_dph_d;
|
||||
reg bus_active_dph_s;
|
||||
|
||||
// clk_always_on is used because SBA may access the bus through this arbiter
|
||||
// whilst the core is asleep (same is not true for I-side interface)
|
||||
|
||||
always @ (posedge clk_always_on or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
bus_hold_aph <= 1'b0;
|
||||
bus_gnt_ds_prev <= 2'h0;
|
||||
end else begin
|
||||
bus_hold_aph <= d_htrans[1] && !d_hready && !d_hresp;
|
||||
bus_gnt_ds_prev <= {bus_gnt_d, bus_gnt_s};
|
||||
end
|
||||
end
|
||||
|
||||
assign {bus_gnt_d, bus_gnt_s} =
|
||||
bus_hold_aph ? bus_gnt_ds_prev :
|
||||
core_aph_req_d ? 2'b10 :
|
||||
dbg_sbus_vld && !bus_active_dph_s ? 2'b01 :
|
||||
2'b00 ;
|
||||
|
||||
always @ (posedge clk_always_on or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
bus_active_dph_d <= 1'b0;
|
||||
bus_active_dph_s <= 1'b0;
|
||||
end else if (d_hready) begin
|
||||
bus_active_dph_d <= bus_gnt_d;
|
||||
bus_active_dph_s <= bus_gnt_s;
|
||||
end
|
||||
end
|
||||
|
||||
assign d_htrans = bus_gnt_d || bus_gnt_s ? HTRANS_NSEQ : HTRANS_IDLE;
|
||||
|
||||
assign d_haddr = bus_gnt_s ? dbg_sbus_addr : core_haddr_d;
|
||||
assign d_hwrite = bus_gnt_s ? dbg_sbus_write : core_hwrite_d;
|
||||
assign d_hsize = bus_gnt_s ? {1'b0, dbg_sbus_size} : core_hsize_d;
|
||||
assign d_hexcl = bus_gnt_s ? 1'b0 : core_aph_excl_d;
|
||||
|
||||
assign d_hprot = {
|
||||
2'b00, // Noncacheable/nonbufferable
|
||||
bus_gnt_s || core_priv_d, // Privileged or Normal as per core state
|
||||
1'b1 // Data access
|
||||
};
|
||||
|
||||
assign d_hwdata = bus_active_dph_s ? dbg_sbus_wdata : core_wdata_d;
|
||||
|
||||
// D-side errors are reported even when not ready, so that the core can make
|
||||
// use of the two-phase error response to cleanly squash a second load/store
|
||||
// chasing the faulting one down the pipeline.
|
||||
assign core_aph_ready_d = d_hready && bus_gnt_d;
|
||||
assign core_dph_ready_d = bus_active_dph_d && d_hready;
|
||||
assign core_dph_err_d = bus_active_dph_d && d_hresp;
|
||||
assign core_dph_exokay_d = bus_active_dph_d && d_hexokay;
|
||||
assign core_rdata_d = d_hrdata;
|
||||
|
||||
assign dbg_sbus_err = bus_active_dph_s && d_hresp;
|
||||
assign dbg_sbus_rdy = bus_active_dph_s && d_hready;
|
||||
assign dbg_sbus_rdata = d_hrdata;
|
||||
|
||||
assign d_hburst = 3'h0;
|
||||
assign d_hmastlock = 1'b0;
|
||||
assign d_hmaster = bus_gnt_s ? 8'h01 : 8'h00;
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
Vendored
+1642
File diff suppressed because it is too large
Load Diff
+204
@@ -0,0 +1,204 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// List of addresses for CSRs implemented by Hazard3, including custom CSRs.
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// M-mode CSRs
|
||||
|
||||
// Machine Information Registers (RO)
|
||||
localparam MVENDORID = 12'hf11; // Vendor ID.
|
||||
localparam MARCHID = 12'hf12; // Architecture ID.
|
||||
localparam MIMPID = 12'hf13; // Implementation ID.
|
||||
localparam MHARTID = 12'hf14; // Hardware thread ID.
|
||||
localparam MCONFIGPTR = 12'hf15; // Pointer to configuration data structure.
|
||||
|
||||
// Machine Trap Setup (RW)
|
||||
localparam MSTATUS = 12'h300; // Machine status register.
|
||||
localparam MSTATUSH = 12'h310; // As of priv-1.12 this must be present even if tied 0.
|
||||
localparam MISA = 12'h301; // ISA and extensions
|
||||
localparam MEDELEG = 12'h302; // Machine exception delegation register.
|
||||
localparam MIDELEG = 12'h303; // Machine interrupt delegation register.
|
||||
localparam MIE = 12'h304; // Machine interrupt-enable register.
|
||||
localparam MTVEC = 12'h305; // Machine trap-handler base address.
|
||||
localparam MCOUNTEREN = 12'h306; // Machine counter enable.
|
||||
|
||||
// Machine Trap Handling (RW)
|
||||
localparam MSCRATCH = 12'h340; // Scratch register for machine trap handlers.
|
||||
localparam MEPC = 12'h341; // Machine exception program counter.
|
||||
localparam MCAUSE = 12'h342; // Machine trap cause.
|
||||
localparam MTVAL = 12'h343; // Machine bad address or instruction.
|
||||
localparam MIP = 12'h344; // Machine interrupt pending.
|
||||
|
||||
// Machine Memory Protection (RW)
|
||||
localparam PMPCFG0 = 12'h3a0; // Physical memory protection configuration.
|
||||
localparam PMPCFG1 = 12'h3a1; // Physical memory protection configuration, RV32 only.
|
||||
localparam PMPCFG2 = 12'h3a2; // Physical memory protection configuration.
|
||||
localparam PMPCFG3 = 12'h3a3; // Physical memory protection configuration, RV32 only.
|
||||
localparam PMPADDR0 = 12'h3b0; // Physical memory protection address register.
|
||||
localparam PMPADDR1 = 12'h3b1; // ...
|
||||
localparam PMPADDR2 = 12'h3b2;
|
||||
localparam PMPADDR3 = 12'h3b3;
|
||||
localparam PMPADDR4 = 12'h3b4;
|
||||
localparam PMPADDR5 = 12'h3b5;
|
||||
localparam PMPADDR6 = 12'h3b6;
|
||||
localparam PMPADDR7 = 12'h3b7;
|
||||
localparam PMPADDR8 = 12'h3b8;
|
||||
localparam PMPADDR9 = 12'h3b9;
|
||||
localparam PMPADDR10 = 12'h3ba;
|
||||
localparam PMPADDR11 = 12'h3bb;
|
||||
localparam PMPADDR12 = 12'h3bc;
|
||||
localparam PMPADDR13 = 12'h3bd;
|
||||
localparam PMPADDR14 = 12'h3be;
|
||||
localparam PMPADDR15 = 12'h3bf;
|
||||
|
||||
localparam MSECCFG = 12'h747;
|
||||
localparam MSECCFGH = 12'h757;
|
||||
|
||||
// Performance counters (RW)
|
||||
localparam MCYCLE = 12'hb00; // Raw cycles since start of day
|
||||
localparam MINSTRET = 12'hb02; // Instruction retire count since start of day
|
||||
localparam MHPMCOUNTER3 = 12'hb03; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER4 = 12'hb04; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER5 = 12'hb05; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER6 = 12'hb06; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER7 = 12'hb07; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER8 = 12'hb08; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER9 = 12'hb09; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER10 = 12'hb0a; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER11 = 12'hb0b; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER12 = 12'hb0c; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER13 = 12'hb0d; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER14 = 12'hb0e; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER15 = 12'hb0f; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER16 = 12'hb10; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER17 = 12'hb11; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER18 = 12'hb12; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER19 = 12'hb13; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER20 = 12'hb14; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER21 = 12'hb15; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER22 = 12'hb16; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER23 = 12'hb17; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER24 = 12'hb18; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER25 = 12'hb19; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER26 = 12'hb1a; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER27 = 12'hb1b; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER28 = 12'hb1c; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER29 = 12'hb1d; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER30 = 12'hb1e; // WARL (we tie to 0)
|
||||
localparam MHPMCOUNTER31 = 12'hb1f; // WARL (we tie to 0)
|
||||
|
||||
localparam MCYCLEH = 12'hb80; // High halves of each counter
|
||||
localparam MINSTRETH = 12'hb82;
|
||||
localparam MHPMCOUNTER3H = 12'hb83;
|
||||
localparam MHPMCOUNTER4H = 12'hb84;
|
||||
localparam MHPMCOUNTER5H = 12'hb85;
|
||||
localparam MHPMCOUNTER6H = 12'hb86;
|
||||
localparam MHPMCOUNTER7H = 12'hb87;
|
||||
localparam MHPMCOUNTER8H = 12'hb88;
|
||||
localparam MHPMCOUNTER9H = 12'hb89;
|
||||
localparam MHPMCOUNTER10H = 12'hb8a;
|
||||
localparam MHPMCOUNTER11H = 12'hb8b;
|
||||
localparam MHPMCOUNTER12H = 12'hb8c;
|
||||
localparam MHPMCOUNTER13H = 12'hb8d;
|
||||
localparam MHPMCOUNTER14H = 12'hb8e;
|
||||
localparam MHPMCOUNTER15H = 12'hb8f;
|
||||
localparam MHPMCOUNTER16H = 12'hb90;
|
||||
localparam MHPMCOUNTER17H = 12'hb91;
|
||||
localparam MHPMCOUNTER18H = 12'hb92;
|
||||
localparam MHPMCOUNTER19H = 12'hb93;
|
||||
localparam MHPMCOUNTER20H = 12'hb94;
|
||||
localparam MHPMCOUNTER21H = 12'hb95;
|
||||
localparam MHPMCOUNTER22H = 12'hb96;
|
||||
localparam MHPMCOUNTER23H = 12'hb97;
|
||||
localparam MHPMCOUNTER24H = 12'hb98;
|
||||
localparam MHPMCOUNTER25H = 12'hb99;
|
||||
localparam MHPMCOUNTER26H = 12'hb9a;
|
||||
localparam MHPMCOUNTER27H = 12'hb9b;
|
||||
localparam MHPMCOUNTER28H = 12'hb9c;
|
||||
localparam MHPMCOUNTER29H = 12'hb9d;
|
||||
localparam MHPMCOUNTER30H = 12'hb9e;
|
||||
localparam MHPMCOUNTER31H = 12'hb9f;
|
||||
|
||||
localparam MCOUNTINHIBIT = 12'h320; // Count inhibit register for mcycle/minstret
|
||||
localparam MHPMEVENT3 = 12'h323; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT4 = 12'h324; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT5 = 12'h325; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT6 = 12'h326; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT7 = 12'h327; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT8 = 12'h328; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT9 = 12'h329; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT10 = 12'h32a; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT11 = 12'h32b; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT12 = 12'h32c; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT13 = 12'h32d; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT14 = 12'h32e; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT15 = 12'h32f; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT16 = 12'h330; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT17 = 12'h331; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT18 = 12'h332; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT19 = 12'h333; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT20 = 12'h334; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT21 = 12'h335; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT22 = 12'h336; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT23 = 12'h337; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT24 = 12'h338; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT25 = 12'h339; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT26 = 12'h33a; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT27 = 12'h33b; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT28 = 12'h33c; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT29 = 12'h33d; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT30 = 12'h33e; // WARL (we tie to 0)
|
||||
localparam MHPMEVENT31 = 12'h33f; // WARL (we tie to 0)
|
||||
|
||||
// Other standard M-mode CSRs:
|
||||
localparam MENVCFG = 12'h30a;
|
||||
localparam MENVCFGH = 12'h31a;
|
||||
|
||||
// Custom M-mode CSRs:
|
||||
localparam PMPCFGM0 = 12'hbd0; // Make PMP regions M-mode without locking
|
||||
// bd1 // (reserved for >32 regions)
|
||||
|
||||
localparam MEIEA = 12'hbe0; // External interrupt pending array
|
||||
localparam MEIPA = 12'hbe1; // External interrupt enable array
|
||||
localparam MEIFA = 12'hbe2; // External interrupt force array
|
||||
localparam MEIPRA = 12'hbe3; // External interrupt priority array
|
||||
localparam MEINEXT = 12'hbe4; // Next external interrupt
|
||||
localparam MEICONTEXT = 12'hbe5; // External interrupt context register
|
||||
|
||||
localparam MSLEEP = 12'hbf0; // M-mode sleep control register
|
||||
localparam H3MISA = 12'hbf1; // Hazard3 M-mode ISA identification register
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// U-mode CSRs
|
||||
|
||||
// Read-only aliases of M-mode counter CSRs:
|
||||
localparam CYCLE = 12'hc00;
|
||||
localparam TIME = 12'hc01;
|
||||
localparam INSTRET = 12'hc02;
|
||||
localparam CYCLEH = 12'hc80;
|
||||
localparam TIMEH = 12'hc81;
|
||||
localparam INSTRETH = 12'hc82;
|
||||
|
||||
// Custom U-mode CSRs
|
||||
localparam SLEEP = 12'h8f0; // U-mode subset of M-mode sleep control
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Trigger Module
|
||||
|
||||
localparam TSELECT = 12'h7a0;
|
||||
localparam TDATA1 = 12'h7a1;
|
||||
localparam TDATA2 = 12'h7a2;
|
||||
localparam TDATA3 = 12'h7a3;
|
||||
localparam TINFO = 12'h7a4;
|
||||
localparam TCONTROL = 12'h7a5;
|
||||
localparam MCONTEXT = 12'h7a8;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// D-mode CSRs
|
||||
|
||||
localparam DCSR = 12'h7b0;
|
||||
localparam DPC = 12'h7b1;
|
||||
localparam DMDATA0 = 12'hbff; // Custom read/write
|
||||
Vendored
+594
@@ -0,0 +1,594 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2023 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_decode #(
|
||||
`include "hazard3_config.vh"
|
||||
,
|
||||
`include "hazard3_width_const.vh"
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
|
||||
input wire [31:0] fd_cir,
|
||||
input wire [1:0] fd_cir_err,
|
||||
input wire [1:0] fd_cir_predbranch,
|
||||
input wire [1:0] fd_cir_vld,
|
||||
input wire fd_cir_is_32bit,
|
||||
input wire fd_cir_invalid_16bit,
|
||||
input wire fd_cir_is_uop,
|
||||
input wire fd_cir_uop_nonfinal,
|
||||
input wire fd_cir_uop_no_pc_update,
|
||||
input wire fd_cir_uop_atomic,
|
||||
|
||||
output wire [1:0] df_cir_use,
|
||||
output wire df_cir_flush_behind,
|
||||
|
||||
output wire df_uop_stall,
|
||||
output wire df_uop_clear,
|
||||
output wire df_lspair_phase_next,
|
||||
|
||||
output wire [W_ADDR-1:0] d_pc,
|
||||
|
||||
input wire debug_mode,
|
||||
input wire m_mode,
|
||||
input wire trap_wfi,
|
||||
|
||||
input wire [W_ADDR-1:0] debug_dpc_wdata,
|
||||
input wire debug_dpc_wen,
|
||||
output wire [W_ADDR-1:0] debug_dpc_rdata,
|
||||
|
||||
output wire d_starved,
|
||||
input wire x_stall,
|
||||
input wire f_jump_now,
|
||||
input wire [W_ADDR-1:0] f_jump_target,
|
||||
input wire x_jump_not_except,
|
||||
input wire [W_ADDR-1:0] d_btb_target_addr,
|
||||
|
||||
output reg [W_DATA-1:0] d_imm,
|
||||
output reg [W_REGADDR-1:0] d_rs1,
|
||||
output reg [W_REGADDR-1:0] d_rs2,
|
||||
output reg [W_REGADDR-1:0] d_rd,
|
||||
output reg [2:0] d_funct3_32b,
|
||||
output reg [6:0] d_funct7_32b,
|
||||
output reg [W_ALUSRC-1:0] d_alusrc_a,
|
||||
output reg [W_ALUSRC-1:0] d_alusrc_b,
|
||||
output reg [W_ALUOP-1:0] d_aluop,
|
||||
output reg [W_MEMOP-1:0] d_memop,
|
||||
output reg [W_MULOP-1:0] d_mulop,
|
||||
output reg d_csr_ren,
|
||||
output reg d_csr_wen,
|
||||
output reg [1:0] d_csr_wtype,
|
||||
output reg d_csr_w_imm,
|
||||
output reg [W_BCOND-1:0] d_branchcond,
|
||||
output reg [W_ADDR-1:0] d_addr_offs,
|
||||
output reg d_addr_is_regoffs,
|
||||
output reg [W_EXCEPT-1:0] d_except,
|
||||
output reg d_sleep_wfi,
|
||||
output reg d_sleep_block,
|
||||
output reg d_sleep_unblock,
|
||||
output wire d_no_pc_increment,
|
||||
output wire d_uninterruptible,
|
||||
output wire [W_ADDR-1:0] d_lspair_offset,
|
||||
output reg d_fence_i,
|
||||
output reg d_fence_d
|
||||
);
|
||||
|
||||
`include "rv_opcodes.vh"
|
||||
`include "hazard3_ops.vh"
|
||||
|
||||
localparam HAVE_CSR = CSR_M_MANDATORY || CSR_M_TRAP || CSR_COUNTER;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
wire [31:0] d_instr = fd_cir | {
|
||||
30'd0, {2{~|EXTENSION_C}}
|
||||
};
|
||||
|
||||
reg d_invalid_32bit;
|
||||
wire d_invalid = fd_cir_invalid_16bit || d_invalid_32bit;
|
||||
|
||||
assign d_uninterruptible = |EXTENSION_ZCMP && fd_cir_uop_atomic;
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
always @ (posedge clk) if (rst_n) begin
|
||||
assert(!(d_invalid && fd_cir_is_uop));
|
||||
assert(!(d_invalid && fd_cir_uop_atomic));
|
||||
end
|
||||
`endif
|
||||
|
||||
wire d_lspair_nonfinal;
|
||||
// Signal to null the mepc offset when taking an exception on this
|
||||
// instruction (because uops in a sequence *which can except*, so excluding
|
||||
// the final sp adjust on popret/popretz, will all have the same PC as the
|
||||
// next uop, which will be in stage 2 when they take their exception)
|
||||
assign d_no_pc_increment = fd_cir_uop_nonfinal || d_lspair_nonfinal;
|
||||
|
||||
assign df_uop_stall = x_stall || d_starved;
|
||||
|
||||
// Note !df_cir_flush_behind because the jump in cm.popret/popretz is the
|
||||
// *penultimate* instruction: we execute the stack adjustment in the fetch
|
||||
// bubble to save a cycle, still need to finish the uop sequence.
|
||||
//
|
||||
// The sp adjust cannot generate an exception (it's an `add` with the same
|
||||
// PMP.X and breakpoint comparison results as earlier uops) and interrupts are
|
||||
// suppressed for this part of the sequence.
|
||||
assign df_uop_clear = f_jump_now && !df_cir_flush_behind;
|
||||
|
||||
// Decode various immediate formats
|
||||
wire [31:0] d_imm_i = {{21{d_instr[31]}}, d_instr[30:20]};
|
||||
wire [31:0] d_imm_s = {{21{d_instr[31]}}, d_instr[30:25], d_instr[11:7]};
|
||||
wire [31:0] d_imm_b = {{20{d_instr[31]}}, d_instr[7], d_instr[30:25], d_instr[11:8], 1'b0};
|
||||
wire [31:0] d_imm_u = {d_instr[31:12], {12{1'b0}}};
|
||||
wire [31:0] d_imm_j = {{12{d_instr[31]}}, d_instr[19:12], d_instr[20], d_instr[30:21], 1'b0};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PC/CIR control
|
||||
|
||||
// Must not flag bus error for a valid 16-bit instruction *followed by* an
|
||||
// error, because instruction fetch errors are speculative, and can be
|
||||
// flushed by e.g. a branch instruction. Note the 16 LSBs must be valid for
|
||||
// us to know an instruction's size.
|
||||
wire d_except_instr_bus_fault = fd_cir_vld > 2'd0 && fd_cir_err[0] ||
|
||||
fd_cir_vld > 2'd1 && fd_cir_is_32bit && fd_cir_err[1];
|
||||
|
||||
assign d_starved = ~|fd_cir_vld || fd_cir_vld[0] && fd_cir_is_32bit;
|
||||
|
||||
wire d_stall = x_stall || d_starved || fd_cir_uop_nonfinal || d_lspair_nonfinal;
|
||||
|
||||
assign df_cir_use =
|
||||
d_starved || d_stall ? 2'h0 :
|
||||
fd_cir_is_32bit ? 2'h2 : 2'h1;
|
||||
|
||||
// CIR Locking is required if we successfully assert a jump request, but
|
||||
// decode is stalled. It is not possible to gate the jump request if the
|
||||
// stall depends on bus stall (as this would create a through-path from bus
|
||||
// stall to bus request) so instead we instruct the frontend to preserve the
|
||||
// stalled instruction when flushing, and fill in behind it.
|
||||
//
|
||||
// Once the stall clears, the stalled instruction can execute its remaining
|
||||
// side effects e.g. writing a link value to the register file.
|
||||
wire jump_caused_by_d = f_jump_now && x_jump_not_except;
|
||||
wire assert_cir_lock = jump_caused_by_d && d_stall;
|
||||
|
||||
// CIR lock ends naturally when an instruction (not just uop) graduates to the
|
||||
// next stage:
|
||||
wire finished_cir_lock = !d_stall;
|
||||
|
||||
// CIR lock can meet an untimely end due to trap entry. One way to reach this
|
||||
// is a dphase load fault on the final load in a cm.popret: here the `ret`
|
||||
// issues a fetch address while stalled on the first dphase cycle, then is
|
||||
// flushed by trap on second cycle.
|
||||
wire deassert_cir_lock = finished_cir_lock || (f_jump_now && !x_jump_not_except);
|
||||
|
||||
reg cir_lock_prev;
|
||||
wire cir_lock = (cir_lock_prev && !deassert_cir_lock) || assert_cir_lock;
|
||||
assign df_cir_flush_behind = assert_cir_lock && !cir_lock_prev;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
cir_lock_prev <= 1'b0;
|
||||
end else begin
|
||||
cir_lock_prev <= cir_lock;
|
||||
end
|
||||
end
|
||||
|
||||
reg [W_ADDR-1:0] pc;
|
||||
wire [W_ADDR-1:0] pc_seq_next = pc + (
|
||||
|EXTENSION_ZCMP && fd_cir_is_uop && fd_cir_uop_no_pc_update ? 32'd0 :
|
||||
fd_cir_is_32bit ? 32'd4 : 32'd2
|
||||
);
|
||||
|
||||
assign d_pc = pc;
|
||||
assign debug_dpc_rdata = pc;
|
||||
|
||||
// Frontend should mark the whole instruction, and nothing but the
|
||||
// instruction, as a predicted branch. This goes wrong when we execute the
|
||||
// address containing the predicted branch twice with different 16-bit
|
||||
// alignments (!). We need to issue a branch-to-self to get back on a linear
|
||||
// path, otherwise PC and CIR will diverge and we will misexecute.
|
||||
wire partial_predicted_branch = !d_starved &&
|
||||
|BRANCH_PREDICTOR && fd_cir_is_32bit && ^fd_cir_predbranch;
|
||||
|
||||
wire predicted_branch = |BRANCH_PREDICTOR && fd_cir_predbranch[0];
|
||||
|
||||
// Generally locking takes place on a stalled jump/branch, which may need the
|
||||
// original PC available to produce a link address when it unstalls. An
|
||||
// exception to this is jumps in micro-op sequences: in this case the jump is
|
||||
// the penultimate instruction in the sequence (ret before addi sp) and we
|
||||
// need to capture the pc mid-uop-sequence.
|
||||
wire hold_pc_on_cir_lock = assert_cir_lock && !(fd_cir_is_uop && !fd_cir_uop_no_pc_update && !x_stall);
|
||||
wire update_pc_on_cir_unlock = cir_lock_prev && finished_cir_lock && !fd_cir_uop_no_pc_update;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
pc <= RESET_VECTOR;
|
||||
end else begin
|
||||
if (debug_dpc_wen) begin
|
||||
pc <= debug_dpc_wdata;
|
||||
end else if (debug_mode) begin
|
||||
pc <= pc;
|
||||
end else if ((f_jump_now && !hold_pc_on_cir_lock) || update_pc_on_cir_unlock) begin
|
||||
pc <= f_jump_target;
|
||||
end else if (!f_jump_now && fd_cir_uop_nonfinal && !fd_cir_uop_no_pc_update && !x_stall) begin
|
||||
// End of previously stalled jr uop in cm.popret and cm.popretz:
|
||||
// safe to update PC as next instruction (addi sp) cannot trap.
|
||||
pc <= f_jump_target;
|
||||
end else if (!d_stall && !cir_lock) begin
|
||||
// If this instruction is a predicted-taken branch (and has not
|
||||
// generated a mispredict recovery jump) then set PC to the
|
||||
// prediction target instead of the sequentially next PC
|
||||
pc <= predicted_branch ? d_btb_target_addr : pc_seq_next;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
always @ (posedge clk) if (rst_n) begin
|
||||
if (~|fd_cir_vld) assert(!fd_cir_is_uop);
|
||||
if (fd_cir_uop_no_pc_update) assert(fd_cir_is_uop);
|
||||
if (fd_cir_uop_nonfinal) assert(fd_cir_is_uop);
|
||||
if ($past(df_uop_clear)) assert(!fd_cir_is_uop);
|
||||
// Important to avoid spurious PC updates following a trap on the final
|
||||
// load of a cm.popret:
|
||||
if ($past(df_uop_clear)) assert(!fd_cir_uop_no_pc_update);
|
||||
end
|
||||
`endif
|
||||
|
||||
wire [W_ADDR-1:0] branch_offs =
|
||||
!fd_cir_is_32bit && predicted_branch ? 32'd2 :
|
||||
fd_cir_is_32bit && predicted_branch ? 32'd4 : d_imm_b;
|
||||
|
||||
always @ (*) begin
|
||||
casez ({|EXTENSION_A, d_instr[6:2]})
|
||||
{1'bz, 5'b11011}: d_addr_offs = d_imm_j ; // JAL
|
||||
{1'bz, 5'b11000}: d_addr_offs = branch_offs ; // Branches
|
||||
{1'bz, 5'b01000}: d_addr_offs = d_imm_s ; // Store
|
||||
{1'bz, 5'b11001}: d_addr_offs = d_imm_i ; // JALR
|
||||
{1'bz, 5'b00000}: d_addr_offs = d_imm_i ; // Loads
|
||||
{1'b1, 5'b01011}: d_addr_offs = 32'h0000_0000; // Atomics
|
||||
default: d_addr_offs = 32'hxxxx_xxxx;
|
||||
endcase
|
||||
if (partial_predicted_branch) begin
|
||||
d_addr_offs = 32'h0000_0000;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Track phase of load/store pair instructions (Zilsd and Zclsd)
|
||||
|
||||
// This could be shared with uop_ctr (for Zcmp) but the two are fundamentally
|
||||
// different: Zcmp has 16-bit instructions which expand to sequences of
|
||||
// 32-bit, whereas Zilsd has multi-phase 32-bit instructions and Zclsd has
|
||||
// direct 16-bit aliases of those instructions. Therefore it's cleaner to
|
||||
// separate the phasing from the decompression for Zilsd/Zclsd.
|
||||
|
||||
wire d_lspair_phase;
|
||||
|
||||
// Reorder accesses to avoid clobbering rs1 (base) in first half of load:
|
||||
wire d_lspair_reg_sel = d_lspair_phase == d_instr[15];
|
||||
|
||||
generate
|
||||
if (EXTENSION_ZILSD) begin: have_lspair_reg_sel
|
||||
reg d_lspair_phase_r;
|
||||
assign d_lspair_phase = d_lspair_phase_r;
|
||||
reg instr_is_lspair;
|
||||
always @ (*) begin
|
||||
casez ({d_invalid || d_starved, d_instr})
|
||||
{1'b0, `RVOPC_LD}: instr_is_lspair = 1'b1;
|
||||
{1'b0, `RVOPC_SD}: instr_is_lspair = 1'b1;
|
||||
default: instr_is_lspair = 1'b0;
|
||||
endcase
|
||||
end
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
d_lspair_phase_r <= 1'b0;
|
||||
end else begin
|
||||
d_lspair_phase_r <= df_lspair_phase_next;
|
||||
end
|
||||
end
|
||||
|
||||
assign df_lspair_phase_next =
|
||||
!d_stall || f_jump_now ? 1'b0 :
|
||||
instr_is_lspair && !x_stall ? 1'b1 : d_lspair_phase_r;
|
||||
|
||||
assign d_lspair_nonfinal = instr_is_lspair && !d_lspair_phase_r;
|
||||
|
||||
assign d_lspair_offset = {
|
||||
29'h0,
|
||||
d_lspair_reg_sel && instr_is_lspair,
|
||||
2'h0
|
||||
};
|
||||
|
||||
end else begin: no_lspair_reg_sel
|
||||
|
||||
assign d_lspair_phase = 1'b0;
|
||||
assign df_lspair_phase_next = 1'b0;
|
||||
assign d_lspair_nonfinal = 1'b0;
|
||||
assign d_lspair_offset = 32'd0;
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Decode X controls
|
||||
|
||||
localparam X0 = {W_REGADDR{1'b0}};
|
||||
|
||||
// First decode the instruction bits, based on available extensions and
|
||||
// privilege state, without gating in any stall/exception signals.
|
||||
reg [W_REGADDR-1:0] raw_rs1;
|
||||
reg [W_REGADDR-1:0] raw_rs2;
|
||||
reg [W_REGADDR-1:0] raw_rd;
|
||||
reg [W_DATA-1:0] raw_imm;
|
||||
reg [W_ALUSRC-1:0] raw_alusrc_a;
|
||||
reg [W_ALUSRC-1:0] raw_alusrc_b;
|
||||
reg [W_ALUOP-1:0] raw_aluop;
|
||||
reg [W_MEMOP-1:0] raw_memop;
|
||||
reg [W_MULOP-1:0] raw_mulop;
|
||||
reg raw_csr_ren;
|
||||
reg raw_csr_wen;
|
||||
reg [1:0] raw_csr_wtype;
|
||||
reg raw_csr_w_imm;
|
||||
reg [W_BCOND-1:0] raw_branchcond;
|
||||
reg raw_addr_is_regoffs;
|
||||
reg [W_EXCEPT-1:0] raw_except;
|
||||
reg raw_sleep_wfi;
|
||||
reg raw_sleep_block;
|
||||
reg raw_sleep_unblock;
|
||||
reg raw_fence_i;
|
||||
reg raw_fence_d;
|
||||
|
||||
always @ (*) begin
|
||||
// Assign some defaults
|
||||
raw_rs1 = d_instr[19:15];
|
||||
raw_rs2 = d_instr[24:20];
|
||||
raw_rd = d_instr[11: 7];
|
||||
raw_imm = d_imm_i;
|
||||
raw_alusrc_a = ALUSRCA_RS1;
|
||||
raw_alusrc_b = ALUSRCB_RS2;
|
||||
raw_aluop = ALUOP_ADD;
|
||||
raw_memop = MEMOP_NONE;
|
||||
raw_mulop = M_OP_MUL;
|
||||
raw_csr_ren = 1'b0;
|
||||
raw_csr_wen = 1'b0;
|
||||
raw_csr_wtype = CSR_WTYPE_W;
|
||||
raw_csr_w_imm = 1'b0;
|
||||
raw_branchcond = BCOND_NEVER;
|
||||
raw_addr_is_regoffs = 1'b0;
|
||||
raw_except = EXCEPT_NONE;
|
||||
raw_sleep_wfi = 1'b0;
|
||||
raw_sleep_block = 1'b0;
|
||||
raw_sleep_unblock = 1'b0;
|
||||
raw_fence_i = 1'b0;
|
||||
raw_fence_d = 1'b0;
|
||||
// Note this funct3/funct7 are valid only for 32-bit instructions. They
|
||||
// are useful for clusters of related ALU ops, such as sh*add, clmul.
|
||||
d_funct3_32b = fd_cir[14:12];
|
||||
d_funct7_32b = fd_cir[31:25];
|
||||
|
||||
d_invalid_32bit = 1'b0;
|
||||
|
||||
casez (d_instr)
|
||||
`RVOPC_BEQ: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_SUB; raw_branchcond = BCOND_ZERO; end
|
||||
`RVOPC_BNE: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_SUB; raw_branchcond = BCOND_NZERO; end
|
||||
`RVOPC_BLT: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_LT; raw_branchcond = BCOND_NZERO; end
|
||||
`RVOPC_BGE: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_LT; raw_branchcond = BCOND_ZERO; end
|
||||
`RVOPC_BLTU: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_LTU; raw_branchcond = BCOND_NZERO; end
|
||||
`RVOPC_BGEU: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_LTU; raw_branchcond = BCOND_ZERO; end
|
||||
`RVOPC_JALR: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_branchcond = BCOND_ALWAYS; raw_addr_is_regoffs = 1'b1;
|
||||
raw_rs2 = X0; raw_aluop = ALUOP_ADD; raw_alusrc_a = ALUSRCA_PC; raw_alusrc_b = ALUSRCB_IMM; raw_imm = fd_cir_is_32bit ? 32'd4 : 32'd2; end
|
||||
`RVOPC_JAL: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_branchcond = BCOND_ALWAYS; raw_rs1 = X0;
|
||||
raw_rs2 = X0; raw_aluop = ALUOP_ADD; raw_alusrc_a = ALUSRCA_PC; raw_alusrc_b = ALUSRCB_IMM; raw_imm = fd_cir_is_32bit ? 32'd4 : 32'd2; end
|
||||
`RVOPC_LUI: begin raw_aluop = ALUOP_RS2; raw_imm = d_imm_u; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; raw_rs1 = X0; end
|
||||
`RVOPC_AUIPC: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_aluop = ALUOP_ADD; raw_imm = d_imm_u; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; raw_alusrc_a = ALUSRCA_PC; raw_rs1 = X0; end
|
||||
`RVOPC_ADDI: begin raw_aluop = ALUOP_ADD; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||
`RVOPC_SLLI: begin raw_aluop = ALUOP_SLL; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||
`RVOPC_SLTI: begin raw_aluop = ALUOP_LT; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||
`RVOPC_SLTIU: begin raw_aluop = ALUOP_LTU; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||
`RVOPC_XORI: begin raw_aluop = ALUOP_XOR; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||
`RVOPC_SRLI: begin raw_aluop = ALUOP_SRL; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||
`RVOPC_SRAI: begin raw_aluop = ALUOP_SRA; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||
`RVOPC_ORI: begin raw_aluop = ALUOP_OR; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||
`RVOPC_ANDI: begin raw_aluop = ALUOP_AND; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||
`RVOPC_ADD: begin raw_aluop = ALUOP_ADD; end
|
||||
`RVOPC_SUB: begin raw_aluop = ALUOP_SUB; end
|
||||
`RVOPC_SLL: begin raw_aluop = ALUOP_SLL; end
|
||||
`RVOPC_SLTU: begin raw_aluop = ALUOP_LTU; end
|
||||
`RVOPC_XOR: begin raw_aluop = ALUOP_XOR; end
|
||||
`RVOPC_SRL: begin raw_aluop = ALUOP_SRL; end
|
||||
`RVOPC_SRA: begin raw_aluop = ALUOP_SRA; end
|
||||
`RVOPC_OR: begin raw_aluop = ALUOP_OR; end
|
||||
`RVOPC_AND: begin raw_aluop = ALUOP_AND; end
|
||||
`RVOPC_LB: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LB; end
|
||||
`RVOPC_LH: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LH; end
|
||||
`RVOPC_LW: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LW; end
|
||||
`RVOPC_LBU: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LBU; end
|
||||
`RVOPC_LHU: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LHU; end
|
||||
`RVOPC_SB: begin raw_addr_is_regoffs = 1'b1; raw_aluop = ALUOP_RS2; raw_memop = MEMOP_SB; raw_rd = X0; end
|
||||
`RVOPC_SH: begin raw_addr_is_regoffs = 1'b1; raw_aluop = ALUOP_RS2; raw_memop = MEMOP_SH; raw_rd = X0; end
|
||||
`RVOPC_SW: begin raw_addr_is_regoffs = 1'b1; raw_aluop = ALUOP_RS2; raw_memop = MEMOP_SW; raw_rd = X0; end
|
||||
|
||||
`RVOPC_SLT: begin
|
||||
raw_aluop = ALUOP_LT;
|
||||
if (|EXTENSION_XH3POWER && ~|raw_rd && ~|raw_rs1) begin
|
||||
if (raw_rs2 == 5'h00) begin
|
||||
// h3.block (power management hint)
|
||||
d_invalid_32bit = trap_wfi;
|
||||
raw_sleep_block = !trap_wfi;
|
||||
end else if (raw_rs2 == 5'h01) begin
|
||||
// h3.unblock (power management hint)
|
||||
d_invalid_32bit = trap_wfi;
|
||||
raw_sleep_unblock = !trap_wfi;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
`RVOPC_MUL: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_MUL; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_MULH: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_MULH; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_MULHSU: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_MULHSU; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_MULHU: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_MULHU; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_DIV: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_DIV; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_DIVU: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_DIVU; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_REM: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_REM; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_REMU: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_REMU; end else begin d_invalid_32bit = 1'b1; end
|
||||
|
||||
`RVOPC_LR_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_LR_W; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_SC_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_SC_W; raw_aluop = ALUOP_RS2; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_AMOSWAP_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_RS2; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_AMOADD_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_ADD; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_AMOXOR_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_XOR; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_AMOAND_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_AND; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_AMOOR_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_OR; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_AMOMIN_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_MIN; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_AMOMAX_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_MAX; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_AMOMINU_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_MINU; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_AMOMAXU_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_MAXU; end else begin d_invalid_32bit = 1'b1; end
|
||||
|
||||
`RVOPC_LD: if (EXTENSION_ZILSD) begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LW; raw_rd = {d_instr[11: 8], d_lspair_reg_sel}; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_SD: if (EXTENSION_ZILSD) begin raw_addr_is_regoffs = 1'b1; raw_rd = X0; raw_memop = MEMOP_SW; raw_rs2 = {d_instr[24:21], d_lspair_reg_sel}; raw_aluop = ALUOP_RS2; end else begin d_invalid_32bit = 1'b1; end
|
||||
|
||||
`RVOPC_SH1ADD: if (EXTENSION_ZBA) begin raw_aluop = ALUOP_SHXADD; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_SH2ADD: if (EXTENSION_ZBA) begin raw_aluop = ALUOP_SHXADD; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_SH3ADD: if (EXTENSION_ZBA) begin raw_aluop = ALUOP_SHXADD; end else begin d_invalid_32bit = 1'b1; end
|
||||
|
||||
`RVOPC_ANDN: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ANDN; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_CLZ: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_CLZ; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_CPOP: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_CPOP; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_CTZ: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_CTZ; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_MAX: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_MAX; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_MAXU: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_MAXU; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_MIN: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_MIN; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_MINU: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_MINU; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_ORC_B: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ORC_B; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_ORN: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ORN; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_REV8: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_REV8; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_ROL: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ROL; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_ROR: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ROR; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_RORI: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ROR; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_SEXT_B: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_SEXT_B; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_SEXT_H: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_SEXT_H; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_XNOR: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_XNOR; end else begin d_invalid_32bit = 1'b1; end
|
||||
// Note: ZEXT_H is a subset of PACK from Zbkb. This is fine as long
|
||||
// as this case appears first, since Zbkb implies Zbb on Hazard3.
|
||||
`RVOPC_ZEXT_H: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ZEXT_H; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
|
||||
`RVOPC_CLMUL: if (EXTENSION_ZBC) begin raw_aluop = ALUOP_CLMUL; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_CLMULH: if (EXTENSION_ZBC) begin raw_aluop = ALUOP_CLMUL; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_CLMULR: if (EXTENSION_ZBC) begin raw_aluop = ALUOP_CLMUL; end else begin d_invalid_32bit = 1'b1; end
|
||||
|
||||
`RVOPC_BCLR: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BCLR; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_BCLRI: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BCLR; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_BEXT: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BEXT; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_BEXTI: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BEXT; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_BINV: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BINV; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_BINVI: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BINV; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_BSET: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BSET; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_BSETI: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BSET; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end
|
||||
|
||||
`RVOPC_PACK: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_PACK; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_PACKH: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_PACKH; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_BREV8: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_BREV8; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_UNZIP: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_UNZIP; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_ZIP: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_ZIP; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
|
||||
`RVOPC_XPERM8: if (EXTENSION_ZBKX) begin raw_aluop = ALUOP_XPERM; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_XPERM4: if (EXTENSION_ZBKX) begin raw_aluop = ALUOP_XPERM; end else begin d_invalid_32bit = 1'b1; end
|
||||
|
||||
`RVOPC_H3_BEXTM: if (EXTENSION_XH3BEXTM) begin
|
||||
raw_aluop = ALUOP_BEXTM; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_H3_BEXTMI: if (EXTENSION_XH3BEXTM) begin
|
||||
raw_aluop = ALUOP_BEXTM; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_CSRRW: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = 1'b1 ; raw_csr_ren = |raw_rd; raw_csr_wtype = CSR_WTYPE_W; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_CSRRS: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = |raw_rs1; raw_csr_ren = 1'b1 ; raw_csr_wtype = CSR_WTYPE_S; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_CSRRC: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = |raw_rs1; raw_csr_ren = 1'b1 ; raw_csr_wtype = CSR_WTYPE_C; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_CSRRWI: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = 1'b1 ; raw_csr_ren = |raw_rd; raw_csr_wtype = CSR_WTYPE_W; raw_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_CSRRSI: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = |raw_rs1; raw_csr_ren = 1'b1 ; raw_csr_wtype = CSR_WTYPE_S; raw_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_CSRRCI: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = |raw_rs1; raw_csr_ren = 1'b1 ; raw_csr_wtype = CSR_WTYPE_C; raw_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end
|
||||
|
||||
`RVOPC_FENCE: begin raw_rs2 = X0; raw_fence_d = 1'b1; end // Note rs1/rd are zero in instruction
|
||||
`RVOPC_FENCE_I: if (EXTENSION_ZIFENCEI) begin raw_except = debug_mode ? EXCEPT_NONE : EXCEPT_REFETCH; raw_fence_i = 1'b1; end else begin d_invalid_32bit = 1'b1; end // note rs1/rs2/rd are zero in instruction
|
||||
`RVOPC_ECALL: if (HAVE_CSR) begin raw_except = m_mode || !U_MODE ? EXCEPT_ECALL_M : EXCEPT_ECALL_U; raw_rs2 = X0; raw_rs1 = X0; raw_rd = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_EBREAK: if (HAVE_CSR) begin raw_except = EXCEPT_EBREAK; raw_rs2 = X0; raw_rs1 = X0; raw_rd = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_MRET: if (HAVE_CSR && m_mode) begin raw_except = EXCEPT_MRET; raw_rs2 = X0; raw_rs1 = X0; raw_rd = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
`RVOPC_WFI: if (HAVE_CSR && !trap_wfi) begin raw_sleep_wfi = 1'b1; raw_rs2 = X0; raw_rs1 = X0; raw_rd = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||
|
||||
default: begin d_invalid_32bit = 1'b1; end
|
||||
endcase
|
||||
|
||||
if (|EXTENSION_E && (raw_rd[4] || raw_rs1[4] || raw_rs2[4])) begin
|
||||
d_invalid_32bit = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// Then gate key signals based on CIR fullness, fetch faults etc. The split
|
||||
// helps to avoid an event scheduling feedback loop that makes simulators
|
||||
// unhappy and slow, particularly verilator
|
||||
|
||||
localparam [4:0] REGADDR_MASK = {~|EXTENSION_E, 4'hf};
|
||||
|
||||
always @ (*) begin
|
||||
// Pass through by default
|
||||
d_rs1 = raw_rs1 & REGADDR_MASK;
|
||||
d_rs2 = raw_rs2 & REGADDR_MASK;
|
||||
d_rd = raw_rd & REGADDR_MASK;
|
||||
d_imm = raw_imm;
|
||||
d_alusrc_a = raw_alusrc_a;
|
||||
d_alusrc_b = raw_alusrc_b;
|
||||
d_aluop = raw_aluop;
|
||||
d_memop = raw_memop;
|
||||
d_mulop = raw_mulop;
|
||||
d_csr_ren = raw_csr_ren;
|
||||
d_csr_wen = raw_csr_wen;
|
||||
d_csr_wtype = raw_csr_wtype;
|
||||
d_csr_w_imm = raw_csr_w_imm;
|
||||
d_branchcond = raw_branchcond;
|
||||
d_addr_is_regoffs = raw_addr_is_regoffs;
|
||||
d_except = raw_except;
|
||||
d_sleep_wfi = raw_sleep_wfi;
|
||||
d_sleep_block = raw_sleep_block;
|
||||
d_sleep_unblock = raw_sleep_unblock;
|
||||
d_fence_i = raw_fence_i;
|
||||
d_fence_d = raw_fence_d;
|
||||
|
||||
if (d_invalid || d_starved || d_except_instr_bus_fault || partial_predicted_branch) begin
|
||||
d_rs1 = {W_REGADDR{1'b0}};
|
||||
d_rs2 = {W_REGADDR{1'b0}};
|
||||
d_rd = {W_REGADDR{1'b0}};
|
||||
d_memop = MEMOP_NONE;
|
||||
d_branchcond = BCOND_NEVER;
|
||||
d_csr_ren = 1'b0;
|
||||
d_csr_wen = 1'b0;
|
||||
d_except = EXCEPT_NONE;
|
||||
d_sleep_wfi = 1'b0;
|
||||
d_sleep_block = 1'b0;
|
||||
d_sleep_unblock = 1'b0;
|
||||
d_fence_i = 1'b0;
|
||||
d_fence_d = 1'b0;
|
||||
|
||||
if (EXTENSION_M)
|
||||
d_aluop = ALUOP_ADD;
|
||||
|
||||
if (d_except_instr_bus_fault)
|
||||
d_except = EXCEPT_INSTR_FAULT;
|
||||
else if (d_invalid && !d_starved)
|
||||
d_except = EXCEPT_INSTR_ILLEGAL;
|
||||
end
|
||||
if (partial_predicted_branch) begin
|
||||
d_addr_is_regoffs = 1'b0;
|
||||
d_branchcond = BCOND_ALWAYS;
|
||||
end
|
||||
if (cir_lock_prev) begin
|
||||
d_branchcond = BCOND_NEVER;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+906
@@ -0,0 +1,906 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_frontend #(
|
||||
`include "hazard3_config.vh"
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
|
||||
// Fetch interface
|
||||
// addr_vld may be asserted at any time, but after assertion,
|
||||
// neither addr nor addr_vld may change until the cycle after addr_rdy.
|
||||
// There is no backpressure on the data interface; the front end
|
||||
// must ensure it does not request data it cannot receive.
|
||||
// addr_rdy and dat_vld may be functions of hready, and
|
||||
// may not be used to compute combinational outputs.
|
||||
output wire mem_size, // 1'b1 -> 32 bit access
|
||||
output wire [W_ADDR-1:0] mem_addr,
|
||||
output wire mem_priv,
|
||||
output wire mem_addr_vld,
|
||||
input wire mem_addr_rdy,
|
||||
input wire [W_DATA-1:0] mem_data,
|
||||
input wire mem_data_err,
|
||||
input wire mem_data_vld,
|
||||
|
||||
// Jump/flush interface
|
||||
// Processor may assert vld at any time. The request will not go through
|
||||
// unless rdy is high. Processor *may* alter request during this time.
|
||||
// Inputs must not be a function of hready.
|
||||
input wire [W_ADDR-1:0] jump_target,
|
||||
input wire jump_priv,
|
||||
input wire jump_target_vld,
|
||||
output wire jump_target_rdy,
|
||||
|
||||
// Interface to the branch target buffer. `src_addr` is the address of the
|
||||
// last halfword of a taken backward branch. The frontend redirects fetch
|
||||
// such that `src_addr` appears to be sequentially followed by `target`.
|
||||
input wire btb_set,
|
||||
input wire [W_ADDR-1:0] btb_set_src_addr,
|
||||
input wire btb_set_src_size,
|
||||
input wire [W_ADDR-1:0] btb_set_target_addr,
|
||||
input wire btb_clear,
|
||||
output wire [W_ADDR-1:0] btb_target_addr_out,
|
||||
|
||||
// Interface to Decode
|
||||
output reg [31:0] cir, // Current instruction register; pre-expanded to 32-bit
|
||||
output wire [31:0] cir_raw, // Unexpanded instruction data
|
||||
output reg [1:0] cir_vld, // number of valid halfwords in CIR
|
||||
input wire [1:0] cir_use, // number of halfwords D intends to consume
|
||||
// *may* be a function of hready
|
||||
output wire [1:0] cir_err, // Bus error on upper/lower halfword of CIR.
|
||||
output wire [1:0] cir_predbranch, // Set for last halfword of a predicted-taken branch
|
||||
output wire cir_break_any, // Set for exact match of a breakpoint address on CIR LSB
|
||||
output wire cir_break_d_mode, // As above but specifically break to debug mode
|
||||
output reg cir_is_32bit, // Can't be decoded from CIR due to pre-expansion
|
||||
output reg cir_invalid_16bit, // Expanded an invalid 32-bit instruction
|
||||
output reg cir_is_uop, // Current instruction is part of a micro-op sequence
|
||||
output reg cir_uop_nonfinal, // ...and there are more to follow in this instruction
|
||||
output reg cir_uop_no_pc_update, // Suppress PC increment or jump (note the jump in cm.popret is not the final uop!)
|
||||
output reg cir_uop_atomic, // Prevent IRQ entry, so intermediate states are not observed
|
||||
input wire uop_stall,
|
||||
input wire uop_clear,
|
||||
|
||||
// "flush_behind": do not flush the oldest instruction when accepting a
|
||||
// jump request (but still flush younger instructions). Sometimes a
|
||||
// stalled instruction may assert a jump request, because e.g. the stall
|
||||
// is dependent on a bus stall signal so can't gate the request.
|
||||
input wire cir_flush_behind,
|
||||
// Required for regnum predecode when Zilsd is enabled:
|
||||
input wire df_lspair_phase_next,
|
||||
|
||||
// Signal to power controller that power down is safe. (When going to
|
||||
// sleep, first the pipeline is stalled, and then the power controller
|
||||
// waits for the frontend to naturally come to a halt before releasing
|
||||
// its power request. This avoids manually halting the frontend.)
|
||||
output wire pwrdown_ok,
|
||||
// Signal to delay the first instruction fetch following reset, because
|
||||
// powerup has not yet been negotiated.
|
||||
input wire delay_first_fetch,
|
||||
|
||||
// Provide the rs1/rs2 register numbers which will be in CIR next cycle.
|
||||
// Coarse: valid if this instruction has a nonzero register operand.
|
||||
// (Suitable for regfile read)
|
||||
output reg [4:0] predecode_rs1_coarse,
|
||||
output reg [4:0] predecode_rs2_coarse,
|
||||
// Fine: like coarse, but accurate zeroing when the operand is implicit.
|
||||
// (Suitable for bypass. Still not precise enough for stall logic.)
|
||||
output reg [4:0] predecode_rs1_fine,
|
||||
output reg [4:0] predecode_rs2_fine,
|
||||
|
||||
// Debugger instruction injection: instruction fetch is suppressed when in
|
||||
// debug halt state, and the DM can then inject instructions into the last
|
||||
// entry of the prefetch queue using the vld/rdy handshake.
|
||||
input wire debug_mode,
|
||||
input wire [W_DATA-1:0] dbg_instr_data,
|
||||
input wire dbg_instr_data_vld,
|
||||
output wire dbg_instr_data_rdy,
|
||||
|
||||
// PMP query->kill interface for X permission checks
|
||||
output wire [W_ADDR-1:0] pmp_i_addr,
|
||||
output wire pmp_i_m_mode,
|
||||
input wire pmp_i_kill,
|
||||
|
||||
// Trigger unit query->break interface for breakpoints
|
||||
output wire [W_ADDR-1:0] trigger_addr,
|
||||
output wire trigger_m_mode,
|
||||
input wire [1:0] trigger_break_any,
|
||||
input wire [1:0] trigger_break_d_mode
|
||||
|
||||
);
|
||||
|
||||
`include "rv_opcodes.vh"
|
||||
|
||||
localparam W_BUNDLE = 16;
|
||||
// This is the minimum for full throughput (enough to avoid dropping data when
|
||||
// decode stalls) and there is no significant advantage to going larger.
|
||||
localparam FIFO_DEPTH = 2;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Fetch queue
|
||||
|
||||
wire jump_now = jump_target_vld && jump_target_rdy;
|
||||
reg [1:0] mem_data_hwvld;
|
||||
|
||||
// PMP X faults are checked in parallel with the fetch (fine if executable
|
||||
// memory is read-idempotent) and failures are promoted to bus errors:
|
||||
wire pmp_kill_fetch_dph;
|
||||
wire mem_or_pmp_err = mem_data_err || pmp_kill_fetch_dph;
|
||||
|
||||
// Similarly, breakpoint matches are checked during fetch data phase. These
|
||||
// are called mem_xxx because they are the breakpoint metadata for the data
|
||||
// coming back from memory in this dphase.
|
||||
wire [1:0] mem_break_any;
|
||||
wire [1:0] mem_break_d_mode;
|
||||
|
||||
// Mark data as containing a predicted-taken branch instruction so that
|
||||
// mispredicts can be recovered -- need to track both halfwords so that we
|
||||
// can mark the entire instruction, and nothing but the instruction:
|
||||
reg [1:0] mem_data_predbranch;
|
||||
|
||||
// Bus errors (and other metadata) travel alongside data. They cause an
|
||||
// exception if the core decodes the instruction, but until then can be
|
||||
// flushed harmlessly.
|
||||
|
||||
reg [W_DATA-1:0] fifo_mem [0:FIFO_DEPTH];
|
||||
reg fifo_err [0:FIFO_DEPTH];
|
||||
reg [1:0] fifo_break_any [0:FIFO_DEPTH];
|
||||
reg [1:0] fifo_break_d_mode [0:FIFO_DEPTH];
|
||||
reg [1:0] fifo_predbranch [0:FIFO_DEPTH];
|
||||
reg [1:0] fifo_valid_hw [0:FIFO_DEPTH];
|
||||
reg fifo_valid [0:FIFO_DEPTH];
|
||||
reg fifo_valid_m1 [0:FIFO_DEPTH];
|
||||
|
||||
wire [W_DATA-1:0] fifo_rdata = fifo_mem[0];
|
||||
wire fifo_full = fifo_valid[FIFO_DEPTH - 1];
|
||||
wire fifo_empty = !fifo_valid[0];
|
||||
wire fifo_almost_full = fifo_valid[FIFO_DEPTH - 2];
|
||||
|
||||
wire fifo_push;
|
||||
wire fifo_pop;
|
||||
wire fifo_dbg_inject = DEBUG_SUPPORT && dbg_instr_data_vld && dbg_instr_data_rdy;
|
||||
|
||||
always @ (*) begin: boundary_conditions
|
||||
integer i;
|
||||
fifo_mem[FIFO_DEPTH] = mem_data;
|
||||
fifo_predbranch[FIFO_DEPTH] = 2'b00;
|
||||
fifo_err[FIFO_DEPTH] = 1'b0;
|
||||
fifo_break_any[FIFO_DEPTH] = 2'b00;
|
||||
fifo_break_d_mode[FIFO_DEPTH] = 2'b00;
|
||||
for (i = 0; i <= FIFO_DEPTH; i = i + 1) begin
|
||||
fifo_valid[i] = |EXTENSION_C ? |fifo_valid_hw[i] : fifo_valid_hw[i][0];
|
||||
// valid-to-right condition: i == 0 || fifo_valid[i - 1], but without
|
||||
// using negative array bound (seems broken in Yosys?) or OOB in the
|
||||
// short circuit case (gives lint although result is well-defined)
|
||||
if (i == 0) begin
|
||||
fifo_valid_m1[i] = 1'b1;
|
||||
end else begin
|
||||
fifo_valid_m1[i] = fifo_valid[i - 1];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin: fifo_update
|
||||
integer i;
|
||||
if (!rst_n) begin
|
||||
for (i = 0; i < FIFO_DEPTH; i = i + 1) begin
|
||||
fifo_valid_hw[i] <= 2'b00;
|
||||
fifo_mem[i] <= 32'd0;
|
||||
fifo_err[i] <= 1'b0;
|
||||
fifo_break_any[i] <= 2'b00;
|
||||
fifo_break_d_mode[i] <= 2'b00;
|
||||
fifo_predbranch[i] <= 2'b00;
|
||||
end
|
||||
// This exists only for loop boundary conditions, but is tied off in
|
||||
// this synchronous process to work around a Verilator scheduling
|
||||
// issue (see issue #21)
|
||||
fifo_valid_hw[FIFO_DEPTH] <= 2'b00;
|
||||
end else begin
|
||||
for (i = 0; i < FIFO_DEPTH; i = i + 1) begin
|
||||
if (fifo_pop || (fifo_push && !fifo_valid[i])) begin
|
||||
fifo_mem[i] <= fifo_valid[i + 1] ? fifo_mem[i + 1] : mem_data;
|
||||
fifo_err[i] <= fifo_valid[i + 1] ? fifo_err[i + 1] : mem_or_pmp_err;
|
||||
fifo_break_any[i] <= fifo_valid[i + 1] ? fifo_break_any[i + 1] : mem_break_any;
|
||||
fifo_break_d_mode[i] <= fifo_valid[i + 1] ? fifo_break_d_mode[i + 1] : mem_break_d_mode;
|
||||
fifo_predbranch[i] <= fifo_valid[i + 1] ? fifo_predbranch[i + 1] : mem_data_predbranch;
|
||||
end
|
||||
fifo_valid_hw[i] <=
|
||||
jump_now ? 2'h0 :
|
||||
fifo_valid[i + 1] && fifo_pop ? fifo_valid_hw[i + 1] :
|
||||
fifo_valid[i] && fifo_pop ? mem_data_hwvld & {2{fifo_push}} :
|
||||
fifo_valid[i] ? fifo_valid_hw[i] :
|
||||
fifo_push && !fifo_pop && fifo_valid_m1[i] ? mem_data_hwvld : 2'h0;
|
||||
end
|
||||
// Allow DM to inject instructions directly into the lowest-numbered
|
||||
// queue entry. This mux should not extend critical path since it is
|
||||
// balanced with the instruction-assembly muxes on the queue bypass
|
||||
// path. Note that flush takes precedence over debug injection
|
||||
// (and the debug module design must account for this)
|
||||
if (fifo_dbg_inject) begin
|
||||
fifo_mem[0] <= dbg_instr_data;
|
||||
fifo_err[0] <= 1'b0;
|
||||
fifo_predbranch[0] <= 2'b00;
|
||||
fifo_break_any[0] <= 2'b00;
|
||||
fifo_break_d_mode[0] <= 2'b00;
|
||||
fifo_valid_hw[0] <= jump_now ? 2'b00 : 2'b11;
|
||||
end
|
||||
fifo_valid_hw[FIFO_DEPTH] <= 2'b00;
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
always @ (posedge clk) if (rst_n) begin
|
||||
// FIFO validity must be compact, so we can always consume from the end
|
||||
if (!fifo_valid[0]) begin
|
||||
assert(!fifo_valid[1]);
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
assign pwrdown_ok = (fifo_full && !jump_target_vld) || debug_mode;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Branch target buffer
|
||||
|
||||
wire [W_ADDR-1:0] btb_src_addr;
|
||||
wire btb_src_size;
|
||||
wire [W_ADDR-1:0] btb_target_addr;
|
||||
wire btb_valid;
|
||||
|
||||
generate
|
||||
if (BRANCH_PREDICTOR) begin: have_btb
|
||||
reg [W_ADDR-1:0] btb_src_addr_r;
|
||||
reg btb_src_size_r;
|
||||
reg [W_ADDR-1:0] btb_target_addr_r;
|
||||
reg btb_valid_r;
|
||||
assign btb_src_addr = btb_src_addr_r;
|
||||
assign btb_src_size = btb_src_size_r;
|
||||
assign btb_target_addr = btb_target_addr_r;
|
||||
assign btb_valid = btb_valid_r;
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
btb_src_addr_r <= {W_ADDR{1'b0}};
|
||||
btb_src_size_r <= 1'b0;
|
||||
btb_target_addr_r <= {W_ADDR{1'b0}};
|
||||
btb_valid_r <= 1'b0;
|
||||
end else if (btb_clear) begin
|
||||
// Clear takes precedences over set. E.g. if a taken branch is in
|
||||
// stage 2 and an exception is in stage 3, we must clear the BTB.
|
||||
btb_valid_r <= 1'b0;
|
||||
end else if (btb_set) begin
|
||||
btb_src_addr_r <= btb_set_src_addr;
|
||||
btb_src_size_r <= btb_set_src_size;
|
||||
btb_target_addr_r <= btb_set_target_addr;
|
||||
btb_valid_r <= 1'b1;
|
||||
end
|
||||
end
|
||||
end else begin: no_btb
|
||||
assign btb_src_addr = {W_ADDR{1'b0}};
|
||||
assign btb_src_size = 1'b0;
|
||||
assign btb_target_addr = {W_ADDR{1'b0}};
|
||||
assign btb_valid = 1'b0;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// Decode uses the target address to set the PC to the correct branch target
|
||||
// value following a predicted-taken branch (as normally it would update PC
|
||||
// by following an X jump request, and in this case there is none).
|
||||
//
|
||||
// Note this assumes the BTB target has not changed by the time the predicted
|
||||
// branch arrives at decode! This is always true because the only way for the
|
||||
// target address to change is when an older branch is taken, which would
|
||||
// flush the younger predicted-taken branch before it reaches decode.
|
||||
|
||||
assign btb_target_addr_out = btb_target_addr;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Fetch request generation
|
||||
|
||||
// Fetch addr runs ahead of the PC, in word increments.
|
||||
reg [W_ADDR-1:0] fetch_addr;
|
||||
reg fetch_priv;
|
||||
reg btb_prev_start_of_overhanging;
|
||||
reg [1:0] mem_aph_hwvld;
|
||||
reg mem_addr_hold;
|
||||
|
||||
wire btb_match_word = |BRANCH_PREDICTOR && btb_valid && (
|
||||
fetch_addr[W_ADDR-1:2] == btb_src_addr[W_ADDR-1:2]
|
||||
);
|
||||
|
||||
// Catch case where predicted-taken branch instruction extends into next word:
|
||||
wire btb_src_overhanging = btb_src_size && btb_src_addr[1];
|
||||
|
||||
// Suppress case where we have jumped immediately after a word-aligned halfword-sized
|
||||
// branch, and the jump target went into fetch_addr due to an address-phase hold:
|
||||
wire btb_jumped_beyond = !btb_src_size && !btb_src_addr[1] && !mem_aph_hwvld[0];
|
||||
|
||||
wire btb_match_current_addr = btb_match_word && !btb_src_overhanging && !btb_jumped_beyond;
|
||||
wire btb_match_next_addr = btb_match_word && btb_src_overhanging;
|
||||
|
||||
wire btb_match_now = btb_match_current_addr || btb_prev_start_of_overhanging;
|
||||
|
||||
// Post-increment if jump request is going straight through
|
||||
wire [W_ADDR-1:0] jump_target_post_increment =
|
||||
{jump_target[W_ADDR-1:2], 2'b00} +
|
||||
{{W_ADDR-3{1'b0}}, mem_addr_rdy && !mem_addr_hold, 2'b00};
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
fetch_addr <= RESET_VECTOR;
|
||||
// M-mode at reset:
|
||||
fetch_priv <= 1'b1;
|
||||
btb_prev_start_of_overhanging <= 1'b0;
|
||||
end else begin
|
||||
if (jump_now) begin
|
||||
fetch_addr <= jump_target_post_increment;
|
||||
fetch_priv <= jump_priv || !U_MODE;
|
||||
btb_prev_start_of_overhanging <= 1'b0;
|
||||
end else if (mem_addr_vld && mem_addr_rdy) begin
|
||||
if (btb_match_now && |BRANCH_PREDICTOR) begin
|
||||
fetch_addr <= {btb_target_addr[W_ADDR-1:2], 2'b00};
|
||||
end else begin
|
||||
fetch_addr <= fetch_addr + 32'd4;
|
||||
end
|
||||
btb_prev_start_of_overhanging <= btb_match_next_addr;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Combinatorially generate the address-phase request
|
||||
|
||||
reg reset_holdoff;
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
reset_holdoff <= 1'b1;
|
||||
end else begin
|
||||
reset_holdoff <= (|EXTENSION_XH3POWER && delay_first_fetch) ? reset_holdoff : 1'b0;
|
||||
// This should be impossible, but assert to be sure, because it *will*
|
||||
// change the fetch address (and we shouldn't check it in hardware if
|
||||
// we can prove it doesn't happen)
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
always @ (posedge clk) if (rst_n) begin
|
||||
assert(!(jump_target_vld && reset_holdoff));
|
||||
end
|
||||
`endif
|
||||
|
||||
reg [W_ADDR-1:0] mem_addr_r;
|
||||
reg mem_priv_r;
|
||||
reg mem_addr_vld_r;
|
||||
|
||||
// Downstream accesses are always word-sized word-aligned.
|
||||
assign mem_addr = mem_addr_r;
|
||||
assign mem_priv = mem_priv_r;
|
||||
assign mem_addr_vld = mem_addr_vld_r && !reset_holdoff;
|
||||
assign mem_size = 1'b1;
|
||||
|
||||
wire fetch_stall;
|
||||
|
||||
always @ (*) begin
|
||||
mem_addr_r = fetch_addr;
|
||||
mem_priv_r = fetch_priv;
|
||||
mem_addr_vld_r = 1'b1;
|
||||
case (1'b1)
|
||||
mem_addr_hold : begin mem_addr_r = fetch_addr; end
|
||||
jump_target_vld || reset_holdoff : begin
|
||||
mem_addr_r = {jump_target[W_ADDR-1:2], 2'b00};
|
||||
mem_priv_r = jump_priv || !U_MODE;
|
||||
end
|
||||
DEBUG_SUPPORT && debug_mode : begin mem_addr_vld_r = 1'b0; end
|
||||
!fetch_stall : begin mem_addr_r = fetch_addr; end
|
||||
default : begin mem_addr_vld_r = 1'b0; end
|
||||
endcase
|
||||
end
|
||||
|
||||
assign jump_target_rdy = !mem_addr_hold;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Bus Pipeline Tracking
|
||||
|
||||
// Keep track of some useful state of the memory interface
|
||||
|
||||
reg [1:0] pending_fetches;
|
||||
reg [1:0] ctr_flush_pending;
|
||||
|
||||
wire [1:0] pending_fetches_next = pending_fetches + (mem_addr_vld && !mem_addr_hold) - mem_data_vld;
|
||||
|
||||
// Using the non-registered version of pending_fetches would improve FIFO
|
||||
// utilisation, but create a combinatorial path from hready to address phase!
|
||||
// This means at least a 2-word FIFO is required for full fetch throughput.
|
||||
assign fetch_stall = fifo_full
|
||||
|| fifo_almost_full && |pending_fetches
|
||||
|| pending_fetches > 2'h1;
|
||||
|
||||
// Debugger only injects instructions when the frontend is at rest and empty.
|
||||
assign dbg_instr_data_rdy = DEBUG_SUPPORT && !fifo_valid[0] && ~|ctr_flush_pending;
|
||||
|
||||
wire cir_room_for_fetch;
|
||||
// If fetch data is forwarded past the FIFO, ensure it is not also written to it.
|
||||
assign fifo_push = mem_data_vld && ~|ctr_flush_pending && !(cir_room_for_fetch && fifo_empty)
|
||||
&& !(DEBUG_SUPPORT && debug_mode);
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
mem_addr_hold <= 1'b0;
|
||||
pending_fetches <= 2'h0;
|
||||
ctr_flush_pending <= 2'h0;
|
||||
end else begin
|
||||
mem_addr_hold <= mem_addr_vld && !mem_addr_rdy;
|
||||
pending_fetches <= pending_fetches_next;
|
||||
if (jump_now) begin
|
||||
ctr_flush_pending <= pending_fetches - mem_data_vld;
|
||||
end else if (|ctr_flush_pending && mem_data_vld) begin
|
||||
ctr_flush_pending <= ctr_flush_pending - 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
always @ (posedge clk) if (rst_n) begin
|
||||
assert(ctr_flush_pending <= pending_fetches);
|
||||
assert(pending_fetches < 2'd3);
|
||||
assert(!(mem_data_vld && !pending_fetches));
|
||||
end
|
||||
`endif
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
mem_data_hwvld <= 2'b11;
|
||||
mem_aph_hwvld <= 2'b11;
|
||||
mem_data_predbranch <= 2'b00;
|
||||
end else begin
|
||||
if (jump_now) begin
|
||||
if (|EXTENSION_C) begin
|
||||
if (mem_addr_rdy) begin
|
||||
mem_aph_hwvld <= 2'b11;
|
||||
mem_data_hwvld <= {1'b1, !jump_target[1]};
|
||||
end else begin
|
||||
mem_aph_hwvld <= {1'b1, !jump_target[1]};
|
||||
end
|
||||
end
|
||||
mem_data_predbranch <= 2'b00;
|
||||
end else if (mem_addr_vld && mem_addr_rdy) begin
|
||||
if (|EXTENSION_C) begin
|
||||
// If a predicted-taken branch instruction only spans the first
|
||||
// half of a word, need to flag the second half as invalid.
|
||||
mem_data_hwvld <= mem_aph_hwvld & {
|
||||
!(|BRANCH_PREDICTOR && btb_match_now && (btb_src_addr[1] == btb_src_size)),
|
||||
1'b1
|
||||
};
|
||||
// Also need to take the alignment of the destination into account.
|
||||
mem_aph_hwvld <= {
|
||||
1'b1,
|
||||
!(|BRANCH_PREDICTOR && btb_match_now && btb_target_addr[1])
|
||||
};
|
||||
end
|
||||
mem_data_predbranch <=
|
||||
|BRANCH_PREDICTOR && btb_match_word ? (
|
||||
btb_src_addr[1] ? 2'b10 :
|
||||
btb_src_size ? 2'b11 : 2'b01
|
||||
) :
|
||||
|BRANCH_PREDICTOR && btb_prev_start_of_overhanging ? (
|
||||
2'b01
|
||||
) : 2'b00;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PMP and trigger unit interfacing: query -> kill/break
|
||||
|
||||
wire [W_ADDR-1:0] pmp_trigger_check_dph_addr;
|
||||
wire pmp_trigger_check_dph_m_mode;
|
||||
|
||||
// Register the fetch address into stage F so that the PMP can check it in
|
||||
// parallel with the bus data phase. Feels wasteful to have a separate
|
||||
// register, but using the fetch_addr counter is fraught due to the way that
|
||||
// new addresses go into it or past it (depending on aphase hold).
|
||||
|
||||
generate
|
||||
if (PMP_REGIONS > 0 || DEBUG_SUPPORT != 0) begin: have_check_reg
|
||||
|
||||
reg [W_ADDR-1:0] check_addr_dph;
|
||||
reg check_m_mode_dph;
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
check_addr_dph <= {W_ADDR{1'b0}};
|
||||
check_m_mode_dph <= 1'b0;
|
||||
end else if (mem_addr_vld && mem_addr_rdy) begin
|
||||
check_addr_dph <= mem_addr;
|
||||
check_m_mode_dph <= mem_priv;
|
||||
end
|
||||
end
|
||||
|
||||
assign pmp_trigger_check_dph_addr = check_addr_dph;
|
||||
assign pmp_trigger_check_dph_m_mode = check_m_mode_dph;
|
||||
|
||||
end else begin: no_check_reg
|
||||
|
||||
assign pmp_trigger_check_dph_addr = {W_ADDR{1'b0}};
|
||||
assign pmp_trigger_check_dph_m_mode = 1'b0;
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
generate
|
||||
if (PMP_REGIONS == 0) begin: no_pmp
|
||||
|
||||
assign pmp_i_addr = {W_ADDR{1'b0}};
|
||||
assign pmp_i_m_mode = 1'b0;
|
||||
assign pmp_kill_fetch_dph = 1'b0;
|
||||
|
||||
end else begin: have_pmp
|
||||
|
||||
assign pmp_i_addr = pmp_trigger_check_dph_addr;
|
||||
assign pmp_i_m_mode = pmp_trigger_check_dph_m_mode;
|
||||
assign pmp_kill_fetch_dph = pmp_i_kill && !debug_mode;
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
generate
|
||||
if (DEBUG_SUPPORT == 0) begin: no_triggers
|
||||
|
||||
assign trigger_addr = {W_ADDR{1'b0}};
|
||||
assign trigger_m_mode = 1'b0;
|
||||
assign mem_break_any = 2'b00;
|
||||
assign mem_break_d_mode = 2'b00;
|
||||
|
||||
end else begin: have_triggers
|
||||
|
||||
assign trigger_addr = pmp_trigger_check_dph_addr;
|
||||
assign trigger_m_mode = pmp_trigger_check_dph_m_mode;
|
||||
assign mem_break_any = trigger_break_any & {|EXTENSION_C, 1'b1};
|
||||
assign mem_break_d_mode = trigger_break_d_mode & {|EXTENSION_C, 1'b1};
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Instruction buffer
|
||||
|
||||
// The instruction buffer is a 3 x ~16-bit shift register:
|
||||
//
|
||||
// * 2 x 16-bit entries form the 32-bit current instruction register (CIR)
|
||||
// which is the processor's decode window
|
||||
//
|
||||
// * 1 x 16-bit entry allows the decode window to be non-32-bit-aligned with
|
||||
// respect to the 2 x 32-bit prefetch queue entries, which are always
|
||||
// naturally aligned in memory (if fully populated).
|
||||
//
|
||||
// The third entry should be trimmed for non-RVC configurations due to
|
||||
// constant-folding on EXTENSION_C; it is unnecessary here because the
|
||||
// instructions are always 32-bit-aligned.
|
||||
|
||||
// The entries ("slots") are slightly larger than 16 bits because they also
|
||||
// contain metadata like bus errors:
|
||||
localparam W_SLOT = 4 + W_BUNDLE;
|
||||
localparam SLOT_BREAK_ANY_BIT = 3 + W_BUNDLE;
|
||||
localparam SLOT_BREAK_D_MODE_BIT = 2 + W_BUNDLE;
|
||||
localparam SLOT_ERR_BIT = 1 + W_BUNDLE;
|
||||
localparam SLOT_PREDBRANCH_BIT = 0 + W_BUNDLE;
|
||||
|
||||
reg [3*W_SLOT-1:0] buf_contents;
|
||||
reg [1:0] buf_level;
|
||||
|
||||
wire fetch_data_vld = !fifo_empty || (mem_data_vld && ~|ctr_flush_pending && !debug_mode);
|
||||
|
||||
wire [W_DATA-1:0] fetch_data = fifo_empty ? mem_data : fifo_rdata;
|
||||
wire [1:0] fetch_data_hwvld = fifo_empty ? mem_data_hwvld : fifo_valid_hw[0];
|
||||
wire fetch_bus_err = fifo_empty ? mem_or_pmp_err : fifo_err[0];
|
||||
wire [1:0] fetch_break_any = fifo_empty ? mem_break_any : fifo_break_any[0];
|
||||
wire [1:0] fetch_break_d_mode = fifo_empty ? mem_break_d_mode : fifo_break_d_mode[0];
|
||||
wire [1:0] fetch_predbranch = fifo_empty ? mem_data_predbranch : fifo_predbranch[0];
|
||||
|
||||
wire [W_SLOT-1:0] fetch_contents_hw1 = {
|
||||
fetch_break_any[1],
|
||||
fetch_break_d_mode[1],
|
||||
fetch_bus_err,
|
||||
fetch_predbranch[1],
|
||||
fetch_data[W_BUNDLE +: W_BUNDLE]
|
||||
};
|
||||
|
||||
wire [W_SLOT-1:0] fetch_contents_hw0 = {
|
||||
fetch_break_any[0],
|
||||
fetch_break_d_mode[0],
|
||||
fetch_bus_err,
|
||||
fetch_predbranch[0],
|
||||
fetch_data[0 +: W_BUNDLE]
|
||||
};
|
||||
|
||||
wire [2*W_SLOT-1:0] fetch_contents_aligned = {
|
||||
fetch_contents_hw1,
|
||||
fetch_data_hwvld[0] || ~|EXTENSION_C ? fetch_contents_hw0 : fetch_contents_hw1
|
||||
};
|
||||
|
||||
// Shift not-yet-used contents down to backfill D's consumption. We don't care
|
||||
// about anything which is invalid or will be overlaid with fresh data, so
|
||||
// choose these values in a way that minimises muxes.
|
||||
wire [3*W_SLOT-1:0] buf_shifted =
|
||||
cir_use[1] ? {buf_contents[W_SLOT +: 2 * W_SLOT], buf_contents[2 * W_SLOT +: W_SLOT]} :
|
||||
cir_use[0] && EXTENSION_C ? {buf_contents[2 * W_SLOT +: W_SLOT], buf_contents[W_SLOT +: 2 * W_SLOT]} :
|
||||
buf_contents;
|
||||
|
||||
wire [1:0] level_next_no_fetch = buf_level - cir_use;
|
||||
|
||||
// Overlay fresh fetch data onto the shifted/recycled buffer contents. Again,
|
||||
// if something won't be looked at, generate the cheapest possible garbage.
|
||||
assign cir_room_for_fetch = level_next_no_fetch <= (|EXTENSION_C && ~&fetch_data_hwvld ? 2'h2 : 2'h1);
|
||||
assign fifo_pop = cir_room_for_fetch && !fifo_empty;
|
||||
|
||||
wire [3*W_SLOT-1:0] buf_shifted_plus_fetch =
|
||||
!cir_room_for_fetch ? buf_shifted :
|
||||
level_next_no_fetch[1] && |EXTENSION_C ? {fetch_contents_aligned[0 +: W_SLOT], buf_shifted[0 +: 2 * W_SLOT]} :
|
||||
level_next_no_fetch[0] && |EXTENSION_C ? {fetch_contents_aligned, buf_shifted[0 +: W_SLOT]} :
|
||||
{buf_shifted[2 * W_SLOT +: W_SLOT], fetch_contents_aligned};
|
||||
|
||||
wire [1:0] fetch_fill_amount = cir_room_for_fetch && fetch_data_vld ? (
|
||||
&fetch_data_hwvld || ~|EXTENSION_C ? 2'h2 : 2'h1
|
||||
) : 2'h0;
|
||||
|
||||
wire [1:0] buf_level_next = {1'b1, |EXTENSION_C} & (
|
||||
jump_now && cir_flush_behind ? (cir_is_32bit ? 2'h2 : 2'h1) :
|
||||
jump_now ? 2'h0 : level_next_no_fetch + fetch_fill_amount
|
||||
);
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
buf_level <= 2'h0;
|
||||
cir_vld <= 2'h0;
|
||||
// Mysterious reset value ensures address buses are zero in reset
|
||||
// (see definition of d_addr_offs in hazard3_decode)
|
||||
buf_contents <= {{3 * W_SLOT - 2{1'b0}}, 2'b11};
|
||||
end else begin
|
||||
buf_level <= buf_level_next;
|
||||
cir_vld <= buf_level_next & ~(buf_level_next >> 1'b1);
|
||||
buf_contents <= buf_shifted_plus_fetch;
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
reg [1:0] prop_past_buf_level; // Workaround for weird non-constant $past reset issue
|
||||
always @ (posedge clk) begin
|
||||
if (!rst_n) begin
|
||||
prop_past_buf_level <= 2'h0;
|
||||
end else begin
|
||||
prop_past_buf_level <= buf_level;
|
||||
|
||||
assert(cir_vld <= 2);
|
||||
assert(cir_use <= cir_vld);
|
||||
if (!jump_now) assert(buf_level_next >= level_next_no_fetch);
|
||||
// We fetch 32 bits per cycle, max. If this happens it's due to negative overflow.
|
||||
if (prop_past_buf_level == 2'h0)
|
||||
assert(buf_level != 2'h3);
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
assign cir_err = {
|
||||
buf_contents[1 * W_SLOT + SLOT_ERR_BIT],
|
||||
buf_contents[0 * W_SLOT + SLOT_ERR_BIT]
|
||||
};
|
||||
|
||||
assign cir_predbranch = {
|
||||
buf_contents[1 * W_SLOT + SLOT_PREDBRANCH_BIT],
|
||||
buf_contents[0 * W_SLOT + SLOT_PREDBRANCH_BIT]
|
||||
};
|
||||
|
||||
assign cir_break_any = buf_contents[0 * W_SLOT + SLOT_BREAK_ANY_BIT] && |cir_vld;
|
||||
|
||||
assign cir_break_d_mode = buf_contents[0 * W_SLOT + SLOT_BREAK_D_MODE_BIT] && |cir_vld;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Register number predecode
|
||||
|
||||
wire [31:0] next_instr = {
|
||||
buf_shifted_plus_fetch[1 * W_SLOT +: W_BUNDLE],
|
||||
buf_shifted_plus_fetch[0 * W_SLOT +: W_BUNDLE]
|
||||
};
|
||||
|
||||
wire next_instr_is_32bit = next_instr[1:0] == 2'b11 || ~|EXTENSION_C;
|
||||
|
||||
wire [3:0] decomp_uop_step;
|
||||
wire [3:0] uop_ctr = decomp_uop_step & {4{|EXTENSION_ZCMP}};
|
||||
|
||||
wire [4:0] zcmp_pushpop_rs2 =
|
||||
uop_ctr == 4'h0 ? 5'd01 : // ra
|
||||
uop_ctr == 4'h1 ? 5'd08 : // s0
|
||||
uop_ctr == 4'h2 ? 5'd09 : // s1
|
||||
5'd15 + {1'b0, uop_ctr} ; // s2-s11
|
||||
|
||||
wire [4:0] zcmp_pushpop_rs1 =
|
||||
uop_ctr < 4'hd ? 5'd02 : // sp (addr base reg)
|
||||
uop_ctr == 4'hd ? 5'd00 : // zero (clear a0)
|
||||
uop_ctr == 4'he ? 5'd01 : // ra (ret)
|
||||
5'd02 ; // sp (stack adj)
|
||||
|
||||
wire [4:0] zcmp_sa01_r1s = {|next_instr[9:8], ~|next_instr[9:8], next_instr[9:7]};
|
||||
wire [4:0] zcmp_sa01_r2s = {|next_instr[4:3], ~|next_instr[4:3], next_instr[4:2]};
|
||||
|
||||
wire [4:0] zcmp_mvsa01_rs1 = {4'h5, uop_ctr[0]};
|
||||
wire [4:0] zcmp_mva01s_rs1 = uop_ctr[0] ? zcmp_sa01_r2s : zcmp_sa01_r1s;
|
||||
|
||||
// "coarse" because the mapping of pair (x0, x1) -> (x0, x0) is not yet applied
|
||||
wire [4:0] zilsd_rs2_coarse = { next_instr[24:21], df_lspair_phase_next ^ ~next_instr[15]};
|
||||
wire [4:0] zclsd_sd_rs2_coarse = {2'b01, next_instr[4:3], df_lspair_phase_next ^ ~next_instr[7] };
|
||||
wire [4:0] zclsd_sdsp_rs2_coarse = { next_instr[6:3], df_lspair_phase_next ^ 1'b1 };
|
||||
|
||||
always @ (*) begin
|
||||
|
||||
casez ({next_instr_is_32bit, |EXTENSION_ZCMP, next_instr[15:0]})
|
||||
{1'b1, 1'bz, 16'bzzzzzzzzzzzzzzzz}: predecode_rs1_coarse = next_instr[19:15]; // 32-bit R, S, B formats
|
||||
{1'b0, 1'bz, 16'b00zzzzzzzzzzzz00}: predecode_rs1_coarse = 5'd2; // c.addi4spn + don't care
|
||||
{1'b0, 1'bz, 16'b0zzzzzzzzzzzzz01}: predecode_rs1_coarse = next_instr[11:7]; // c.addi, c.addi16sp + don't care (jal, li)
|
||||
{1'b0, 1'bz, 16'bz1zzzzzzzzzzzz10}: predecode_rs1_coarse = 5'd2; // c.lwsp, c.swsp, c.ldsp, c.sdsp
|
||||
{1'b0, 1'bz, 16'bz00zzzzzzzzzzz10}: predecode_rs1_coarse = next_instr[11:7]; // c.slli, c.mv, c.add
|
||||
{1'b0, 1'b1, 16'b1011zzzzzzzzzz10}: predecode_rs1_coarse = zcmp_pushpop_rs1; // cm.push, cm.pop*
|
||||
{1'b0, 1'b1, 16'b1010zzzzz0zzzz10}: predecode_rs1_coarse = zcmp_mvsa01_rs1; // cm.mvsa01
|
||||
{1'b0, 1'b1, 16'b1010zzzzz1zzzz10}: predecode_rs1_coarse = zcmp_mva01s_rs1; // cm.mva01s
|
||||
default: predecode_rs1_coarse = {2'b01, next_instr[9:7]};
|
||||
endcase
|
||||
|
||||
casez ({next_instr_is_32bit, |EXTENSION_ZCMP, |EXTENSION_ZILSD, |EXTENSION_ZCLSD, next_instr[15:0]})
|
||||
{1'b1, 1'bz, 1'b1, 1'bz, 16'bzz11zzzzz0z0zzzz}: predecode_rs2_coarse = zilsd_rs2_coarse; // ld, sd (Zilsd)
|
||||
{1'b1, 1'bz, 1'b0, 1'bz, 16'bzz11zzzzz0z0zzzz}: predecode_rs2_coarse = next_instr[24:20]; // ld, sd (no Zilsd)
|
||||
|
||||
{1'b1, 1'bz, 1'bz, 1'bz, 16'bzz0zzzzzzzzzzzzz}: predecode_rs2_coarse = next_instr[24:20]; // (cover remaining 32-bit
|
||||
{1'b1, 1'bz, 1'bz, 1'bz, 16'bzz10zzzzzzzzzzzz}: predecode_rs2_coarse = next_instr[24:20]; // patterns, without overlap)
|
||||
{1'b1, 1'bz, 1'bz, 1'bz, 16'bzz11zzzzz1zzzzzz}: predecode_rs2_coarse = next_instr[24:20];
|
||||
{1'b1, 1'bz, 1'bz, 1'bz, 16'bzz11zzzzz0z1zzzz}: predecode_rs2_coarse = next_instr[24:20];
|
||||
|
||||
{1'b0, 1'bz, 1'b1, 1'b1, 16'bzz1zzzzzzzzzzz00}: predecode_rs2_coarse = zclsd_sd_rs2_coarse;
|
||||
{1'b0, 1'bz, 1'bz, 1'bz, 16'bzz0zzzzzzzzzzz10}: predecode_rs2_coarse = next_instr[6:2]; // c.add, c.swsp
|
||||
{1'b0, 1'b1, 1'bz, 1'bz, 16'bz01zzzzzzzzzzz10}: predecode_rs2_coarse = zcmp_pushpop_rs2; // cm.push
|
||||
{1'b0, 1'bz, 1'b1, 1'b1, 16'bz11zzzzzzzzzzz10}: predecode_rs2_coarse = zclsd_sdsp_rs2_coarse;
|
||||
default: predecode_rs2_coarse = {2'b01, next_instr[4:2]};
|
||||
endcase
|
||||
|
||||
// The "fine" predecode targets those instructions which either:
|
||||
// - Have an implicit zero-register operand in their expanded form (e.g. c.beqz)
|
||||
// - Do not have a register operand on that port, but rely on the port being 0
|
||||
// We don't care about instructions which ignore the reg ports, e.g. ebreak
|
||||
|
||||
casez ({|EXTENSION_C, next_instr})
|
||||
// -> addi rd, x0, imm:
|
||||
{1'b1, 16'hzzzz, `RVOPC_C_LI}: predecode_rs1_fine = 5'd0;
|
||||
{1'b1, 16'hzzzz, `RVOPC_C_MV}: begin
|
||||
if (next_instr[6:2] == 5'd0) begin
|
||||
// c.jr has rs1 as normal
|
||||
predecode_rs1_fine = predecode_rs1_coarse;
|
||||
end else begin
|
||||
// -> add rd, x0, rs2:
|
||||
predecode_rs1_fine = 5'd0;
|
||||
end
|
||||
end
|
||||
default: predecode_rs1_fine = predecode_rs1_coarse;
|
||||
endcase
|
||||
|
||||
casez ({|EXTENSION_C, |EXTENSION_ZILSD, |EXTENSION_ZCLSD, next_instr})
|
||||
{1'b1, 1'bz, 1'bz, 16'hzzzz, `RVOPC_C_BEQZ}: predecode_rs2_fine = 5'd0; // -> beq rs1, x0, label
|
||||
{1'b1, 1'bz, 1'bz, 16'hzzzz, `RVOPC_C_BNEZ}: predecode_rs2_fine = 5'd0; // -> bne rs1, x0, label
|
||||
{1'b1, 1'b1, 1'bz, `RVOPC_SD }: predecode_rs2_fine = predecode_rs2_coarse & {5{|next_instr[24:21]}};
|
||||
{1'b1, 1'b1, 1'b1, 16'hzzzz, `RVOPC_C_SDSP}: predecode_rs2_fine = predecode_rs2_coarse & {5{|next_instr[ 6: 3]}};
|
||||
default: predecode_rs2_fine = predecode_rs2_coarse;
|
||||
endcase
|
||||
|
||||
if (|EXTENSION_E) begin
|
||||
predecode_rs1_coarse[4] = 1'b0;
|
||||
predecode_rs2_coarse[4] = 1'b0;
|
||||
predecode_rs1_fine[4] = 1'b0;
|
||||
predecode_rs2_fine[4] = 1'b0;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Instruction decompression
|
||||
|
||||
// Instructions are decompressed at the end of stage 1 (fetch data phase). On
|
||||
// ASIC, where the register file is synthesised with muxes, this puts
|
||||
// decompression somewhat in parallel with register file read, which uses
|
||||
// approximately decoded regnums.
|
||||
|
||||
generate
|
||||
if (~|EXTENSION_C) begin: no_decompress
|
||||
|
||||
// No decompression; instructions decoded directly from prefetch buffer
|
||||
always @ (*) begin
|
||||
cir = {
|
||||
buf_contents[1 * W_SLOT +: W_BUNDLE],
|
||||
buf_contents[0 * W_SLOT +: W_BUNDLE]
|
||||
} | 32'd3;
|
||||
cir_is_32bit = 1'b1;
|
||||
cir_invalid_16bit = ~&buf_contents[1:0];
|
||||
cir_is_uop = 1'b0;
|
||||
cir_uop_nonfinal = 1'b0;
|
||||
cir_uop_no_pc_update = 1'b0;
|
||||
cir_uop_atomic = 1'b0;
|
||||
end
|
||||
|
||||
assign decomp_uop_step = 4'h0;
|
||||
|
||||
end else begin: have_decompress
|
||||
|
||||
wire decomp_instr_is_32bit;
|
||||
wire [31:0] decomp_instr_out;
|
||||
wire decomp_is_uop;
|
||||
wire decomp_is_final_uop;
|
||||
wire decomp_uop_no_pc_update;
|
||||
wire decomp_uop_atomic;
|
||||
wire decomp_invalid;
|
||||
|
||||
wire first_uop = ~|decomp_uop_step;
|
||||
// Ensure the first uop goes straight through, as it is registered into CIR:
|
||||
wire uop_stall_non_first = first_uop ? ~|buf_level_next : uop_stall;
|
||||
// Ensure the uop counter stops at 0 after rolling over once:
|
||||
wire uop_stall_on_repeat = cir_is_uop && !cir_uop_nonfinal && ~|cir_use;
|
||||
|
||||
hazard3_instr_decompress #(
|
||||
`include "hazard3_config_inst.vh"
|
||||
) decomp (
|
||||
.clk (clk),
|
||||
.rst_n (rst_n),
|
||||
|
||||
.instr_in (next_instr),
|
||||
|
||||
.instr_is_32bit (decomp_instr_is_32bit),
|
||||
.instr_out (decomp_instr_out),
|
||||
|
||||
.instr_out_is_uop (decomp_is_uop),
|
||||
.instr_out_is_final_uop (decomp_is_final_uop),
|
||||
.instr_out_uop_no_pc_update (decomp_uop_no_pc_update),
|
||||
.instr_out_uop_atomic (decomp_uop_atomic),
|
||||
.instr_out_uop_stall (uop_stall_non_first || uop_stall_on_repeat),
|
||||
.instr_out_uop_clear (uop_clear),
|
||||
.df_uop_step (decomp_uop_step),
|
||||
|
||||
.invalid (decomp_invalid)
|
||||
);
|
||||
|
||||
wire cir_clken =
|
||||
~|cir_vld || (!cir_vld[1] && &buf_contents[1:0]) ||
|
||||
|cir_use || (|EXTENSION_ZCMP && cir_is_uop && !uop_stall) ||
|
||||
(|EXTENSION_ZCMP && uop_clear);
|
||||
|
||||
wire cir_is_uop_next = decomp_is_uop && |buf_level_next;
|
||||
wire cir_uop_nonfinal_next = cir_is_uop_next && !decomp_is_final_uop;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
cir <= 32'd3;
|
||||
cir_is_32bit <= 1'b0;
|
||||
cir_invalid_16bit <= 1'b0;
|
||||
cir_is_uop <= 1'b0;
|
||||
cir_uop_nonfinal <= 1'b0;
|
||||
cir_uop_no_pc_update <= 1'b0;
|
||||
cir_uop_atomic <= 1'b0;
|
||||
end else if (cir_clken) begin
|
||||
cir <= decomp_instr_out | 32'd3;
|
||||
cir_is_32bit <= decomp_instr_is_32bit;
|
||||
cir_invalid_16bit <= decomp_invalid;
|
||||
cir_is_uop <= |EXTENSION_ZCMP && !uop_clear && cir_is_uop_next;
|
||||
cir_uop_nonfinal <= |EXTENSION_ZCMP && !uop_clear && cir_uop_nonfinal_next;
|
||||
cir_uop_no_pc_update <= |EXTENSION_ZCMP && !uop_clear && decomp_uop_no_pc_update;
|
||||
cir_uop_atomic <= |EXTENSION_ZCMP && !uop_clear && decomp_uop_atomic;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
assign cir_raw = {
|
||||
buf_contents[1 * W_SLOT +: W_BUNDLE],
|
||||
buf_contents[0 * W_SLOT +: W_BUNDLE]
|
||||
};
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2023 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Little instructions go in, big instructions come out
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_instr_decompress #(
|
||||
`include "hazard3_config.vh"
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
|
||||
input wire [31:0] instr_in,
|
||||
|
||||
output reg instr_is_32bit,
|
||||
output reg [31:0] instr_out,
|
||||
|
||||
// If instruction is a non-final uop, need to suppress PC update, and null
|
||||
// the PC offset in the mepc address in stage 3.
|
||||
output wire instr_out_is_uop,
|
||||
output wire instr_out_is_final_uop,
|
||||
output wire instr_out_uop_no_pc_update,
|
||||
// Indicate instr_out is a uop from the noninterruptible part of a uop
|
||||
// sequence. If one uop is noninterruptible, all following uops until the
|
||||
// end of the sequence are also noninterruptible.
|
||||
output wire instr_out_uop_atomic,
|
||||
// Current ucode sequence is stalled on downstream execution
|
||||
input wire instr_out_uop_stall,
|
||||
input wire instr_out_uop_clear,
|
||||
|
||||
// To regnum decoder in frontend
|
||||
output wire [3:0] df_uop_step,
|
||||
|
||||
output reg invalid
|
||||
);
|
||||
|
||||
`include "rv_opcodes.vh"
|
||||
|
||||
localparam W_REGADDR = 5;
|
||||
localparam PASSTHROUGH = ~|EXTENSION_C;
|
||||
|
||||
// Long-register formats: cr, ci, css
|
||||
// Short-register formats: ciw, cl, cs, cb, cj
|
||||
wire [W_REGADDR-1:0] rd_l = instr_in[11:7];
|
||||
wire [W_REGADDR-1:0] rs1_l = instr_in[11:7];
|
||||
wire [W_REGADDR-1:0] rs2_l = instr_in[6:2];
|
||||
wire [W_REGADDR-1:0] rd_s = {2'b01, instr_in[4:2]};
|
||||
wire [W_REGADDR-1:0] rs1_s = {2'b01, instr_in[9:7]};
|
||||
wire [W_REGADDR-1:0] rs2_s = {2'b01, instr_in[4:2]};
|
||||
|
||||
// Mapping of cx -> x immediate formats (we are *expanding* instructions, not
|
||||
// decoding them):
|
||||
|
||||
wire [31:0] imm_ci = {
|
||||
{7{instr_in[12]}},
|
||||
instr_in[6:2],
|
||||
20'h00000
|
||||
};
|
||||
|
||||
wire [31:0] imm_cj = {
|
||||
instr_in[12],
|
||||
instr_in[8],
|
||||
instr_in[10:9],
|
||||
instr_in[6],
|
||||
instr_in[7],
|
||||
instr_in[2],
|
||||
instr_in[11],
|
||||
instr_in[5:3],
|
||||
{9{instr_in[12]}},
|
||||
12'h000
|
||||
};
|
||||
|
||||
wire [31:0] imm_cb ={
|
||||
{4{instr_in[12]}},
|
||||
instr_in[6:5],
|
||||
instr_in[2],
|
||||
13'h0000,
|
||||
instr_in[11:10],
|
||||
instr_in[4:3],
|
||||
instr_in[12],
|
||||
7'h00
|
||||
};
|
||||
|
||||
wire [31:0] imm_c_lb = {
|
||||
10'h0,
|
||||
instr_in[5],
|
||||
instr_in[6],
|
||||
20'h00000
|
||||
};
|
||||
|
||||
wire [31:0] imm_c_lh = {
|
||||
10'h000,
|
||||
instr_in[5],
|
||||
1'b0,
|
||||
20'h00000
|
||||
};
|
||||
|
||||
function [31:0] rfmt_rd; input [4:0] rd; begin rfmt_rd = {20'h00000, rd, 7'h00}; end endfunction
|
||||
function [31:0] rfmt_rs1; input [4:0] rs1; begin rfmt_rs1 = {12'h000, rs1, 15'h0000}; end endfunction
|
||||
function [31:0] rfmt_rs2; input [4:0] rs2; begin rfmt_rs2 = {7'h00, rs2, 20'h00000}; end endfunction
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Push/pop and friends
|
||||
|
||||
// The longest uop sequence is a maximal cm.popretz:
|
||||
//
|
||||
// - 13x lw (counter = 0..12)
|
||||
// - 1x addi to set a0 to zero (counter = 13 ) < atomic section
|
||||
// - 1x jalr to jump through ra (counter = 14 ) < atomic section
|
||||
// - 1x addi to adjust sp (counter = 15 ) < atomic section
|
||||
|
||||
wire [3:0] uop_ctr;
|
||||
reg [3:0] uop_ctr_nxt_in_seq;
|
||||
reg in_uop_seq;
|
||||
reg uop_no_pc_update;
|
||||
|
||||
wire zcmp_is_pushpop = instr_in[12];
|
||||
wire uop_seq_end = |EXTENSION_ZCMP && (zcmp_is_pushpop ? uop_ctr == 4'hf : uop_ctr[0]);
|
||||
wire uop_atomic = |EXTENSION_ZCMP && (zcmp_is_pushpop ? uop_ctr >= 4'he : uop_ctr[0]);
|
||||
|
||||
wire [3:0] uop_ctr_nxt =
|
||||
instr_out_uop_clear ? 4'h0 :
|
||||
instr_out_uop_stall ? uop_ctr : uop_ctr_nxt_in_seq;
|
||||
|
||||
assign instr_out_is_uop = in_uop_seq;
|
||||
assign instr_out_is_final_uop = uop_seq_end;
|
||||
assign instr_out_uop_atomic = uop_atomic;
|
||||
assign instr_out_uop_no_pc_update = uop_no_pc_update;
|
||||
assign df_uop_step = uop_ctr;
|
||||
|
||||
// The offset from current sp value to the lowest-addressed saved register, +64.
|
||||
wire [3:0] zcmp_rlist = instr_in[7:4];
|
||||
wire [3:0] zcmp_n_regs = zcmp_rlist == 4'hf ? 4'hd : zcmp_rlist - 4'h3;
|
||||
wire zcmp_rlist_invalid = zcmp_rlist < 4'h4 || (|EXTENSION_E && zcmp_rlist > 4'h6);
|
||||
|
||||
wire [11:0] zcmp_stack_adj_base =
|
||||
zcmp_rlist == 4'hf ? 12'h040 :
|
||||
zcmp_rlist >= 4'hc ? 12'h030 :
|
||||
zcmp_rlist >= 4'h8 ? 12'h020 : 12'h010;
|
||||
|
||||
wire [11:0] zcmp_stack_adj = zcmp_stack_adj_base + {6'h00, instr_in[3:2], 4'h0};
|
||||
|
||||
// Note we perform all load/stores before moving the stack pointer.
|
||||
wire [11:0] zcmp_stack_lw_offset = -{6'h00, {zcmp_n_regs - uop_ctr}, 2'h0} + zcmp_stack_adj;
|
||||
wire [11:0] zcmp_stack_sw_offset = -{6'h00, {zcmp_n_regs - uop_ctr}, 2'h0};
|
||||
|
||||
wire [4:0] zcmp_ls_reg =
|
||||
uop_ctr == 4'h0 ? 5'd01 : // ra
|
||||
uop_ctr == 4'h1 ? 5'd08 : // s0
|
||||
uop_ctr == 4'h2 ? 5'd09 : // s1
|
||||
5'd15 + {1'b0, uop_ctr}; // s2-s11 (s2 == x18)
|
||||
|
||||
wire [31:0] zcmp_push_sw_instr = `RVOPC_NOZ_SW | rfmt_rs1(5'd2) | rfmt_rs2(zcmp_ls_reg) | {
|
||||
zcmp_stack_sw_offset[11:5], 13'h0000, zcmp_stack_sw_offset[4:0], 7'h00
|
||||
};
|
||||
|
||||
wire [31:0] zcmp_pop_lw_instr = `RVOPC_NOZ_LW | rfmt_rd(zcmp_ls_reg) | rfmt_rs1(5'd2)| {
|
||||
zcmp_stack_lw_offset[11:0], 20'h00000
|
||||
};
|
||||
|
||||
wire [31:0] zcmp_push_stack_adj_instr = `RVOPC_NOZ_ADDI | rfmt_rd(5'd2) | rfmt_rs1(5'd2) | {
|
||||
-zcmp_stack_adj,
|
||||
20'h00000
|
||||
};
|
||||
|
||||
wire [31:0] zcmp_pop_stack_adj_instr = `RVOPC_NOZ_ADDI | rfmt_rd(5'd2) | rfmt_rs1(5'd2) | {
|
||||
zcmp_stack_adj,
|
||||
20'h00000
|
||||
};
|
||||
|
||||
wire [4:0] zcmp_sa01_r1s = {|instr_in[9:8], ~|instr_in[9:8], instr_in[9:7]};
|
||||
wire [4:0] zcmp_sa01_r2s = {|instr_in[4:3], ~|instr_in[4:3], instr_in[4:2]};
|
||||
|
||||
wire zcmp_sa01_invalid = |EXTENSION_E && |{instr_in[9:8], instr_in[4:3]};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
generate
|
||||
if (PASSTHROUGH) begin: instr_passthrough
|
||||
always @ (*) begin
|
||||
instr_is_32bit = 1'b1;
|
||||
instr_out = instr_in;
|
||||
invalid = 1'b0;
|
||||
end
|
||||
end else begin: instr_decompress
|
||||
always @ (*) begin
|
||||
if (instr_in[1:0] == 2'b11) begin
|
||||
instr_is_32bit = 1'b1;
|
||||
instr_out = instr_in;
|
||||
invalid = 1'b0;
|
||||
in_uop_seq = 1'b0;
|
||||
uop_no_pc_update = 1'b0;
|
||||
uop_ctr_nxt_in_seq = uop_ctr;
|
||||
end else begin
|
||||
instr_is_32bit = 1'b0;
|
||||
instr_out = 32'd0;
|
||||
invalid = 1'b0;
|
||||
in_uop_seq = 1'b0;
|
||||
uop_no_pc_update = 1'b0;
|
||||
uop_ctr_nxt_in_seq = uop_ctr;
|
||||
casez (instr_in[15:0])
|
||||
`RVOPC_C_ADDI4SPN: begin
|
||||
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(rd_s) | rfmt_rs1(5'd2)
|
||||
| {2'h0, instr_in[10:7], instr_in[12:11], instr_in[5], instr_in[6], 2'b00, 20'h00000};
|
||||
invalid = ~|instr_in[12:2]; // Always-invalid all-zeroes instruction
|
||||
end
|
||||
`RVOPC_C_LW: instr_out = `RVOPC_NOZ_LW | rfmt_rd(rd_s) | rfmt_rs1(rs1_s)
|
||||
| {5'h00, instr_in[5], instr_in[12:10], instr_in[6], 2'b00, 20'h00000};
|
||||
`RVOPC_C_SW: instr_out = `RVOPC_NOZ_SW | rfmt_rs2(rs2_s) | rfmt_rs1(rs1_s)
|
||||
| {5'h00, instr_in[5], instr_in[12], 13'h0000, instr_in[11:10], instr_in[6], 2'b00, 7'h00};
|
||||
`RVOPC_C_ADDI: instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(rd_l) | rfmt_rs1(rs1_l) | imm_ci;
|
||||
`RVOPC_C_JAL: instr_out = `RVOPC_NOZ_JAL | rfmt_rd(5'd1) | imm_cj;
|
||||
`RVOPC_C_J: instr_out = `RVOPC_NOZ_JAL | rfmt_rd(5'd0) | imm_cj;
|
||||
`RVOPC_C_LI: instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(rd_l) | imm_ci;
|
||||
`RVOPC_C_LUI: begin
|
||||
if (rd_l == 5'd2) begin
|
||||
// addi16sp
|
||||
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(5'd2) | rfmt_rs1(5'd2) |
|
||||
{{3{instr_in[12]}}, instr_in[4:3], instr_in[5], instr_in[2], instr_in[6], 24'h000000};
|
||||
end else begin
|
||||
instr_out = `RVOPC_NOZ_LUI | rfmt_rd(rd_l) | {{15{instr_in[12]}}, instr_in[6:2], 12'h000};
|
||||
end
|
||||
invalid = ~|{instr_in[12], instr_in[6:2]}; // RESERVED if imm == 0
|
||||
end
|
||||
`RVOPC_C_SLLI: instr_out = `RVOPC_NOZ_SLLI | rfmt_rd(rs1_l) | rfmt_rs1(rs1_l) | imm_ci;
|
||||
`RVOPC_C_SRAI: instr_out = `RVOPC_NOZ_SRAI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | imm_ci;
|
||||
`RVOPC_C_SRLI: instr_out = `RVOPC_NOZ_SRLI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | imm_ci;
|
||||
`RVOPC_C_ANDI: instr_out = `RVOPC_NOZ_ANDI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | imm_ci;
|
||||
`RVOPC_C_AND: instr_out = `RVOPC_NOZ_AND | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s);
|
||||
`RVOPC_C_OR: instr_out = `RVOPC_NOZ_OR | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s);
|
||||
`RVOPC_C_XOR: instr_out = `RVOPC_NOZ_XOR | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s);
|
||||
`RVOPC_C_SUB: instr_out = `RVOPC_NOZ_SUB | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s);
|
||||
`RVOPC_C_ADD: begin
|
||||
if (|rs2_l) begin
|
||||
instr_out = `RVOPC_NOZ_ADD | rfmt_rd(rd_l) | rfmt_rs1(rs1_l) | rfmt_rs2(rs2_l);
|
||||
end else if (|rs1_l) begin // jalr
|
||||
instr_out = `RVOPC_NOZ_JALR | rfmt_rd(5'd1) | rfmt_rs1(rs1_l);
|
||||
end else begin // ebreak
|
||||
instr_out = `RVOPC_NOZ_EBREAK;
|
||||
end
|
||||
end
|
||||
`RVOPC_C_MV: begin
|
||||
if (|rs2_l) begin // mv
|
||||
instr_out = `RVOPC_NOZ_ADD | rfmt_rd(rd_l) | rfmt_rs2(rs2_l);
|
||||
end else begin // jr
|
||||
instr_out = `RVOPC_NOZ_JALR | rfmt_rs1(rs1_l);
|
||||
invalid = ~|rs1_l; // RESERVED
|
||||
end
|
||||
end
|
||||
`RVOPC_C_LWSP: begin
|
||||
instr_out = `RVOPC_NOZ_LW | rfmt_rd(rd_l) | rfmt_rs1(5'd2) |
|
||||
{4'h0, instr_in[3:2], instr_in[12], instr_in[6:4], 2'b00, 20'h00000};
|
||||
invalid = ~|rd_l; // RESERVED
|
||||
end
|
||||
`RVOPC_C_SWSP: instr_out = `RVOPC_NOZ_SW | rfmt_rs2(rs2_l) | rfmt_rs1(5'd2)
|
||||
| {4'h0, instr_in[8:7], instr_in[12], 13'h0000, instr_in[11:9], 2'b00, 7'h00};
|
||||
`RVOPC_C_BEQZ: instr_out = `RVOPC_NOZ_BEQ | rfmt_rs1(rs1_s) | imm_cb;
|
||||
`RVOPC_C_BNEZ: instr_out = `RVOPC_NOZ_BNE | rfmt_rs1(rs1_s) | imm_cb;
|
||||
|
||||
// Optional Zcb instructions:
|
||||
`RVOPC_C_LBU: begin
|
||||
instr_out = `RVOPC_NOZ_LBU | rfmt_rd(rd_s) | rfmt_rs1(rs1_s) | imm_c_lb;
|
||||
invalid = ~|EXTENSION_ZCB;
|
||||
end
|
||||
`RVOPC_C_LHU: begin
|
||||
instr_out = `RVOPC_NOZ_LHU | rfmt_rd(rd_s) | rfmt_rs1(rs1_s) | imm_c_lh;
|
||||
invalid = ~|EXTENSION_ZCB;
|
||||
end
|
||||
`RVOPC_C_LH: begin
|
||||
instr_out = `RVOPC_NOZ_LH | rfmt_rd(rd_s) | rfmt_rs1(rs1_s) | imm_c_lh;
|
||||
invalid = ~|EXTENSION_ZCB;
|
||||
end
|
||||
`RVOPC_C_SB: begin
|
||||
instr_out = `RVOPC_NOZ_SB | rfmt_rs2(rd_s) | rfmt_rs1(rs1_s) | imm_c_lb >> 13;
|
||||
invalid = ~|EXTENSION_ZCB;
|
||||
end
|
||||
`RVOPC_C_SH: begin
|
||||
instr_out = `RVOPC_NOZ_SH | rfmt_rs2(rd_s) | rfmt_rs1(rs1_s) | imm_c_lh >> 13;
|
||||
invalid = ~|EXTENSION_ZCB;
|
||||
end
|
||||
`RVOPC_C_ZEXT_B: begin
|
||||
instr_out = `RVOPC_NOZ_ANDI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | 32'h0ff00000;
|
||||
invalid = ~|EXTENSION_ZCB;
|
||||
end
|
||||
`RVOPC_C_SEXT_B: begin
|
||||
instr_out = `RVOPC_NOZ_SEXT_B | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s);
|
||||
invalid = ~|EXTENSION_ZCB || ~|EXTENSION_ZBB;
|
||||
end
|
||||
`RVOPC_C_ZEXT_H: begin
|
||||
instr_out = `RVOPC_NOZ_ZEXT_H | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s);
|
||||
invalid = ~|EXTENSION_ZCB || ~|EXTENSION_ZBB;
|
||||
end
|
||||
`RVOPC_C_SEXT_H: begin
|
||||
instr_out = `RVOPC_NOZ_SEXT_H | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s);
|
||||
invalid = ~|EXTENSION_ZCB || ~|EXTENSION_ZBB;
|
||||
end
|
||||
`RVOPC_C_NOT: begin
|
||||
instr_out = `RVOPC_NOZ_XORI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | 32'hfff00000;
|
||||
invalid = ~|EXTENSION_ZCB;
|
||||
end
|
||||
`RVOPC_C_MUL: begin
|
||||
instr_out = `RVOPC_NOZ_MUL | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s);
|
||||
invalid = ~|EXTENSION_ZCB || ~|EXTENSION_M;
|
||||
end
|
||||
|
||||
// Optional Zclsd instructions:
|
||||
`RVOPC_C_LD: begin
|
||||
instr_out = `RVOPC_NOZ_LD | rfmt_rd(rd_s) | rfmt_rs1(rs1_s)
|
||||
| {4'h0, instr_in[6:5], instr_in[12:10], 3'b000, 20'h00000};
|
||||
invalid = ~|EXTENSION_ZILSD || ~|EXTENSION_ZCLSD;
|
||||
end
|
||||
`RVOPC_C_SD: begin
|
||||
instr_out = `RVOPC_NOZ_SD | rfmt_rs2(rs2_s) | rfmt_rs1(rs1_s)
|
||||
| {4'h0, instr_in[6:5], instr_in[12], 13'h0000, instr_in[11:10], 3'b000, 7'h00};
|
||||
invalid = ~|EXTENSION_ZILSD || ~|EXTENSION_ZCLSD;
|
||||
end
|
||||
`RVOPC_C_LDSP: begin
|
||||
instr_out = `RVOPC_NOZ_LD | rfmt_rd(rd_l) | rfmt_rs1(5'd2) |
|
||||
{3'h0, instr_in[4:2], instr_in[12], instr_in[6:5], 3'b000, 20'h00000};
|
||||
invalid = ~|EXTENSION_ZILSD || ~|EXTENSION_ZCLSD || ~|rd_l; // RESERVED
|
||||
end
|
||||
`RVOPC_C_SDSP: begin
|
||||
instr_out = `RVOPC_NOZ_SD | rfmt_rs2(rs2_l) | rfmt_rs1(5'd2)
|
||||
| {3'h0, instr_in[9:7], instr_in[12], 13'h0000, instr_in[11:10], 3'b000, 7'h00};
|
||||
invalid = ~|EXTENSION_ZILSD || ~|EXTENSION_ZCLSD;
|
||||
end
|
||||
|
||||
// Optional Zcmp instructions:
|
||||
`RVOPC_CM_PUSH: if (~|EXTENSION_ZCMP || zcmp_rlist_invalid) begin
|
||||
invalid = 1'b1;
|
||||
end else if (uop_ctr == 4'hf) begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = 4'h0;
|
||||
instr_out = zcmp_push_stack_adj_instr;
|
||||
end else begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||
instr_out = zcmp_push_sw_instr;
|
||||
uop_no_pc_update = 1'b1;
|
||||
if (uop_ctr_nxt_in_seq == zcmp_n_regs) begin
|
||||
uop_ctr_nxt_in_seq = 4'hf;
|
||||
end
|
||||
end
|
||||
|
||||
`RVOPC_CM_POP: if (~|EXTENSION_ZCMP || zcmp_rlist_invalid) begin
|
||||
invalid = 1'b1;
|
||||
end else if (uop_ctr == 4'hf) begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = 4'h0;
|
||||
instr_out = zcmp_pop_stack_adj_instr;
|
||||
end else begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||
uop_no_pc_update = 1'b1;
|
||||
instr_out = zcmp_pop_lw_instr;
|
||||
if (uop_ctr_nxt_in_seq == zcmp_n_regs) begin
|
||||
uop_ctr_nxt_in_seq = 4'hf;
|
||||
end
|
||||
end
|
||||
|
||||
`RVOPC_CM_POPRET: if (~|EXTENSION_ZCMP || zcmp_rlist_invalid) begin
|
||||
invalid = 1'b1;
|
||||
end else if (uop_ctr == 4'he) begin
|
||||
// Note although this is only the first instruction in the uninterruptible sequence,
|
||||
// we mark this instruction as uninterruptible: there is some special case logic to
|
||||
// allow this jump to execute without flushing the final stack adjust uop, which can
|
||||
// cause the wrong exception PC to be sampled if this uop is interrupted.
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||
instr_out = `RVOPC_NOZ_JALR | rfmt_rs1(5'd1);
|
||||
end else if (uop_ctr == 4'hf) begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = 4'h0;
|
||||
uop_no_pc_update = 1'b1;
|
||||
instr_out = zcmp_pop_stack_adj_instr;
|
||||
end else begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||
instr_out = zcmp_pop_lw_instr;
|
||||
uop_no_pc_update = 1'b1;
|
||||
if (uop_ctr_nxt_in_seq == zcmp_n_regs) begin
|
||||
uop_ctr_nxt_in_seq = 4'he;
|
||||
end
|
||||
end
|
||||
|
||||
`RVOPC_CM_POPRETZ: if (~|EXTENSION_ZCMP || zcmp_rlist_invalid) begin
|
||||
invalid = 1'b1;
|
||||
end else if (uop_ctr == 4'hd) begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||
uop_no_pc_update = 1'b1;
|
||||
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(5'd10); // li a0, 0
|
||||
end else if (uop_ctr == 4'he) begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||
instr_out = `RVOPC_NOZ_JALR | rfmt_rs1(5'd1);
|
||||
end else if (uop_ctr == 4'hf) begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = 4'h0;
|
||||
uop_no_pc_update = 1'b1;
|
||||
instr_out = zcmp_pop_stack_adj_instr;
|
||||
end else begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||
uop_no_pc_update = 1'b1;
|
||||
instr_out = zcmp_pop_lw_instr;
|
||||
if (uop_ctr_nxt_in_seq == zcmp_n_regs) begin
|
||||
uop_ctr_nxt_in_seq = 4'hd;
|
||||
end
|
||||
end
|
||||
|
||||
`RVOPC_CM_MVSA01: if (~|EXTENSION_ZCMP || zcmp_sa01_invalid) begin
|
||||
invalid = 1'b1;
|
||||
end else if (uop_ctr == 4'h0) begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||
uop_no_pc_update = 1'b1;
|
||||
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(zcmp_sa01_r1s) | rfmt_rs1(5'd10);
|
||||
end else begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = 4'h0;
|
||||
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(zcmp_sa01_r2s) | rfmt_rs1(5'd11);
|
||||
end
|
||||
|
||||
`RVOPC_CM_MVA01S: if (~|EXTENSION_ZCMP || zcmp_sa01_invalid) begin
|
||||
invalid = 1'b1;
|
||||
end else if (uop_ctr == 4'h0) begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||
uop_no_pc_update = 1'b1;
|
||||
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(5'd10) | rfmt_rs1(zcmp_sa01_r1s);
|
||||
end else begin
|
||||
in_uop_seq = 1'b1;
|
||||
uop_ctr_nxt_in_seq = 4'h0;
|
||||
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(5'd11) | rfmt_rs1(zcmp_sa01_r2s);
|
||||
end
|
||||
|
||||
default: invalid = 1'b1;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
generate
|
||||
if (EXTENSION_ZCMP) begin: have_uop_ctr
|
||||
reg [3:0] uop_ctr_r;
|
||||
assign uop_ctr = uop_ctr_r;
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
uop_ctr_r <= 4'h0;
|
||||
end else begin
|
||||
uop_ctr_r <= uop_ctr_nxt;
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
assert(in_uop_seq || uop_ctr_r == 4'h0);
|
||||
assert(in_uop_seq || zcmp_ls_reg == 5'h01);
|
||||
assert(in_uop_seq || !uop_atomic);
|
||||
assert(in_uop_seq || !uop_no_pc_update);
|
||||
if (uop_seq_end) begin
|
||||
assert(in_uop_seq);
|
||||
assert(instr_out_uop_stall || uop_ctr_nxt == 4'h0);
|
||||
end
|
||||
`endif
|
||||
end
|
||||
end
|
||||
end else begin: no_uop_ctr
|
||||
assign uop_ctr = 4'h0;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
// Hazard3 interrupt controller. Support for up to 512 external interrupt
|
||||
// lines, with up to 16 levels of preemption.
|
||||
|
||||
module hazard3_irq_ctrl #(
|
||||
`include "hazard3_config.vh"
|
||||
) (
|
||||
input wire clk,
|
||||
input wire clk_always_on,
|
||||
input wire rst_n,
|
||||
|
||||
// CSR interface
|
||||
input wire [11:0] addr,
|
||||
input wire [1:0] wtype,
|
||||
input wire wen_m_mode,
|
||||
input wire ren_m_mode,
|
||||
input wire [W_DATA-1:0] wdata_raw,
|
||||
input wire [W_DATA-1:0] wdata,
|
||||
output reg [W_DATA-1:0] rdata,
|
||||
|
||||
// Trap entry/exit signals for context update
|
||||
input wire trapreg_update_enter,
|
||||
input wire trapreg_update_exit,
|
||||
input wire trap_entry_is_eirq,
|
||||
|
||||
// Interface for clearing and saving mie.mtie/msie via meicontext
|
||||
output wire meicontext_clearts,
|
||||
input wire mie_mtie,
|
||||
input wire mie_msie,
|
||||
|
||||
// External IRQ inputs:
|
||||
input wire [NUM_IRQS-1:0] irq,
|
||||
|
||||
// mip.meip:
|
||||
output wire external_irq_pending
|
||||
);
|
||||
|
||||
`include "hazard3_ops.vh"
|
||||
`include "hazard3_csr_addr.vh"
|
||||
|
||||
localparam MAX_IRQS = 512;
|
||||
localparam [3:0] IRQ_PRIORITY_MASK = ~(4'hf >> IRQ_PRIORITY_BITS);
|
||||
localparam W_IRQ_INDEX = $clog2(MAX_IRQS);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// IRQ input flops
|
||||
|
||||
// Register external IRQ signals (mainly to avoid a through-path from IRQs to
|
||||
// bus request signals). Always clocked, as it's used to generate a wakeup.
|
||||
// Input registers can be removed on a per-IRQ basis, but this should be done
|
||||
// with care as it does create a through-path from the IRQ to the bus.
|
||||
|
||||
wire [NUM_IRQS-1:0] irq_r;
|
||||
|
||||
genvar g;
|
||||
generate
|
||||
for (g = 0; g < NUM_IRQS; g = g + 1) begin: irq_reg_loop
|
||||
if (IRQ_INPUT_BYPASS[g]) begin: no_reg
|
||||
assign irq_r[g] = irq[g];
|
||||
end else begin: have_reg
|
||||
reg q;
|
||||
always @ (posedge clk_always_on or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
q <= 1'b0;
|
||||
end else begin
|
||||
q <= irq[g];
|
||||
end
|
||||
end
|
||||
assign irq_r[g] = q;
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// CSR write
|
||||
|
||||
// Assigned later:
|
||||
wire [W_IRQ_INDEX-1:0] meinext_irq;
|
||||
wire meinext_noirq;
|
||||
reg [3:0] eirq_highest_priority;
|
||||
|
||||
// Interrupt array registers:
|
||||
reg [NUM_IRQS-1:0] meiea;
|
||||
reg [NUM_IRQS-1:0] meifa;
|
||||
reg [4*NUM_IRQS-1:0] meipra;
|
||||
|
||||
// Padded vectors for CSR readout
|
||||
wire [MAX_IRQS-1:0] meiea_rdata = {{MAX_IRQS-NUM_IRQS{1'b0}}, meiea};
|
||||
wire [MAX_IRQS-1:0] meifa_rdata = {{MAX_IRQS-NUM_IRQS{1'b0}}, meifa};
|
||||
wire [4*MAX_IRQS-1:0] meipra_rdata = {{4*(MAX_IRQS-NUM_IRQS){1'b0}}, meipra};
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin: update_irq_reg_arrays
|
||||
reg signed [31:0] i;
|
||||
if (!rst_n) begin
|
||||
meiea <= {NUM_IRQS{1'b0}};
|
||||
meifa <= {NUM_IRQS{1'b0}};
|
||||
meipra <= {4*NUM_IRQS{1'b0}};
|
||||
end else begin
|
||||
for (i = 0; i < NUM_IRQS; i = i + 1) begin
|
||||
// CSR write update. Note raw wdata is used for array indexing --
|
||||
// necessary for correctness, and also avoid a loop with rdata.
|
||||
if (wen_m_mode && addr == MEIEA && $signed(wdata_raw[4:0]) == i[W_IRQ_INDEX-1:4]) begin
|
||||
meiea[i] <= wdata[16 + (i % 16)];
|
||||
end
|
||||
if (wen_m_mode && addr == MEIFA && $signed(wdata_raw[4:0]) == i[W_IRQ_INDEX-1:4]) begin
|
||||
meifa[i] <= wdata[16 + (i % 16)];
|
||||
end
|
||||
if (wen_m_mode && addr == MEIPRA && $signed(wdata_raw[6:0]) == i[W_IRQ_INDEX-1:2]) begin
|
||||
meipra[4 * i +: 4] <= wdata[16 + 4 * (i % 4) +: 4] & IRQ_PRIORITY_MASK;
|
||||
end
|
||||
// Clear IRQ force when the corresponding IRQ is sampled from meinext
|
||||
// (so that an IRQ can be posted *once* without modifying the ISR source)
|
||||
if (meinext_irq == i[W_IRQ_INDEX-1:0] && ren_m_mode && addr == MEINEXT && !meinext_noirq) begin
|
||||
meifa[i[$clog2(NUM_IRQS)-1:0]] <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
reg [3:0] meicontext_pppreempt;
|
||||
reg [3:0] meicontext_ppreempt;
|
||||
reg [4:0] meicontext_preempt;
|
||||
reg meicontext_noirq;
|
||||
reg [W_IRQ_INDEX-1:0] meicontext_irq;
|
||||
reg meicontext_mreteirq;
|
||||
|
||||
wire [4:0] preempt_level_next = meinext_noirq ? 5'h10 : (
|
||||
(5'd1 << (4 - IRQ_PRIORITY_BITS)) + {1'b0, eirq_highest_priority}
|
||||
);
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
meicontext_pppreempt <= 4'h0;
|
||||
meicontext_ppreempt <= 4'h0;
|
||||
meicontext_preempt <= 5'h0;
|
||||
meicontext_noirq <= 1'b1;
|
||||
meicontext_irq <= {W_IRQ_INDEX{1'b0}};
|
||||
meicontext_mreteirq <= 1'b0;
|
||||
end else if (trapreg_update_enter) begin
|
||||
if (trap_entry_is_eirq) begin
|
||||
// Priority save. Note the MSB of preempt needn't be saved since,
|
||||
// when it is set, preemption is impossible, so we won't be here.
|
||||
meicontext_pppreempt <= meicontext_ppreempt & IRQ_PRIORITY_MASK;
|
||||
meicontext_ppreempt <= meicontext_preempt[3:0] & IRQ_PRIORITY_MASK;
|
||||
// Setting preempt isn't strictly necessary, since an updating read
|
||||
// of meinext ought to be performed before re-enabling IRQs via
|
||||
// mstatus.mie, but it seems the least surprising thing to do:
|
||||
meicontext_preempt <= preempt_level_next & {1'b1, IRQ_PRIORITY_MASK};
|
||||
meicontext_mreteirq <= 1'b1;
|
||||
end else begin
|
||||
meicontext_mreteirq <= 1'b0;
|
||||
end
|
||||
end else if (trapreg_update_exit) begin
|
||||
meicontext_mreteirq <= 1'b0;
|
||||
if (meicontext_mreteirq) begin
|
||||
// Priority restore
|
||||
meicontext_pppreempt <= 4'h0;
|
||||
meicontext_ppreempt <= meicontext_pppreempt & IRQ_PRIORITY_MASK;
|
||||
meicontext_preempt <= {1'b0, meicontext_ppreempt & IRQ_PRIORITY_MASK};
|
||||
end
|
||||
end else if (wen_m_mode && addr == MEICONTEXT) begin
|
||||
meicontext_pppreempt <= wdata[31:28] & IRQ_PRIORITY_MASK;
|
||||
meicontext_ppreempt <= wdata[27:24] & IRQ_PRIORITY_MASK;
|
||||
meicontext_preempt <= wdata[20:16] & {1'b1, IRQ_PRIORITY_MASK};
|
||||
meicontext_noirq <= wdata[15];
|
||||
meicontext_irq <= wdata[12:4];
|
||||
meicontext_mreteirq <= wdata[0];
|
||||
end else if (wen_m_mode && addr == MEINEXT && wdata[0]) begin
|
||||
// Interrupt has been sampled, with the update request set, so update
|
||||
// the context (including preemption level) appropriately.
|
||||
meicontext_preempt <= preempt_level_next & {1'b1, IRQ_PRIORITY_MASK};
|
||||
meicontext_noirq <= meinext_noirq;
|
||||
meicontext_irq <= meinext_irq;
|
||||
end
|
||||
end
|
||||
|
||||
assign meicontext_clearts = wen_m_mode && wtype != CSR_WTYPE_C && addr == MEICONTEXT && wdata_raw[1];
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// External interrupt logic
|
||||
|
||||
// Trap request is asserted when there is an interrupt at or above our current
|
||||
// preemption level. meinext displays interrupts at or above our *previous*
|
||||
// preemption level: this masking helps avoid re-taking IRQs in frames that you
|
||||
// have preempted.
|
||||
|
||||
wire [NUM_IRQS-1:0] meipa = irq_r | meifa;
|
||||
wire [MAX_IRQS-1:0] meipa_rdata = {{MAX_IRQS-NUM_IRQS{1'b0}}, meipa};
|
||||
|
||||
reg [NUM_IRQS-1:0] eirq_active_above_preempt;
|
||||
reg [NUM_IRQS-1:0] eirq_active_above_ppreempt;
|
||||
|
||||
always @ (*) begin: eirq_compare
|
||||
integer i;
|
||||
for (i = 0; i < NUM_IRQS; i = i + 1) begin
|
||||
eirq_active_above_preempt[i] = meipa[i] && meiea[i] && {1'b0, meipra[i * 4 +: 4]} >= meicontext_preempt;
|
||||
eirq_active_above_ppreempt[i] = meipa[i] && meiea[i] && meipra[i * 4 +: 4] >= meicontext_ppreempt;
|
||||
end
|
||||
end
|
||||
|
||||
assign external_irq_pending = |eirq_active_above_preempt;
|
||||
assign meinext_noirq = ~|eirq_active_above_ppreempt;
|
||||
|
||||
// Two things remaining to calculate:
|
||||
//
|
||||
// - What is the IRQ number of the highest-priority pending IRQ that is above
|
||||
// meicontext.ppreempt
|
||||
// - What is the priority of that IRQ
|
||||
//
|
||||
// In the second case we can relax the calculation to ignore ppreempt, since it
|
||||
// only needs to be valid if such an IRQ exists. Currently we choose to reuse
|
||||
// the same priority selector (possibly longer critpath while saving area), but
|
||||
// we could use a second priority selector that ignores ppreempt masking.
|
||||
|
||||
wire [NUM_IRQS-1:0] highest_eirq_onehot;
|
||||
wire [W_IRQ_INDEX-1:0] meinext_irq_unmasked;
|
||||
|
||||
hazard3_onehot_priority_dynamic #(
|
||||
.W_REQ (NUM_IRQS),
|
||||
.N_PRIORITIES (16),
|
||||
.PRIORITY_HIGHEST_WINS (1),
|
||||
.TIEBREAK_HIGHEST_WINS (0)
|
||||
) eirq_priority_u (
|
||||
.pri (meipra[4*NUM_IRQS-1:0] & {NUM_IRQS{IRQ_PRIORITY_MASK}}),
|
||||
.req (eirq_active_above_ppreempt),
|
||||
.gnt (highest_eirq_onehot)
|
||||
);
|
||||
|
||||
always @ (*) begin: get_highest_eirq_priority
|
||||
integer i;
|
||||
eirq_highest_priority = 4'h0;
|
||||
for (i = 0; i < NUM_IRQS; i = i + 1) begin
|
||||
eirq_highest_priority = eirq_highest_priority | (
|
||||
meipra[4 * i +: 4] & {4{highest_eirq_onehot[i]}}
|
||||
);
|
||||
end
|
||||
end
|
||||
|
||||
wire [$clog2(NUM_IRQS)-1:0] meinext_irq_unmasked_nopad;
|
||||
|
||||
hazard3_onehot_encode #(
|
||||
.W_REQ (NUM_IRQS)
|
||||
) eirq_encode_u (
|
||||
.req (highest_eirq_onehot),
|
||||
.gnt (meinext_irq_unmasked_nopad)
|
||||
);
|
||||
|
||||
generate
|
||||
if ($clog2(NUM_IRQS) == $clog2(MAX_IRQS)) begin: encode_eirq_no_padding
|
||||
assign meinext_irq_unmasked = meinext_irq_unmasked_nopad;
|
||||
end else begin: encode_eirq_padded
|
||||
assign meinext_irq_unmasked = {
|
||||
{$clog2(MAX_IRQS) - $clog2(NUM_IRQS){1'b0}},
|
||||
meinext_irq_unmasked_nopad
|
||||
};
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// It is unnecessary to mask meinext_irq based on meinext_noirq because:
|
||||
// - The value of the CSR field is unimportant when noirq is set
|
||||
// - There are no IRQ inputs to the priority selector when there
|
||||
// are no IRQs, so result is already 0.
|
||||
assign meinext_irq = meinext_irq_unmasked;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// CSR read
|
||||
|
||||
always @ (*) begin
|
||||
rdata = {W_DATA{1'b0}};
|
||||
case (addr)
|
||||
|
||||
MEIEA: rdata = {
|
||||
meiea_rdata[wdata_raw[4:0] * 16 +: 16],
|
||||
16'h0
|
||||
};
|
||||
|
||||
MEIPA: rdata = {
|
||||
meipa_rdata[wdata_raw[4:0] * 16 +: 16],
|
||||
16'h0
|
||||
};
|
||||
|
||||
MEIFA: rdata = {
|
||||
meifa_rdata[wdata_raw[4:0] * 16 +: 16],
|
||||
16'h0
|
||||
};
|
||||
|
||||
MEIPRA: rdata = {
|
||||
meipra_rdata[wdata_raw[6:0] * 16 +: 16],
|
||||
16'h0
|
||||
};
|
||||
|
||||
MEINEXT: rdata = {
|
||||
meinext_noirq,
|
||||
20'h0,
|
||||
meinext_irq,
|
||||
2'h0
|
||||
};
|
||||
|
||||
MEICONTEXT: rdata = {
|
||||
meicontext_pppreempt,
|
||||
meicontext_ppreempt,
|
||||
3'h0,
|
||||
meicontext_preempt,
|
||||
meicontext_noirq,
|
||||
2'h0,
|
||||
meicontext_irq,
|
||||
mie_mtie && meicontext_clearts,
|
||||
mie_msie && meicontext_clearts,
|
||||
1'b0,
|
||||
meicontext_mreteirq
|
||||
};
|
||||
|
||||
default: rdata = {W_DATA{1'b0}};
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
Vendored
+124
@@ -0,0 +1,124 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// ALU operation selectors
|
||||
|
||||
localparam ALUOP_ADD = 6'h00;
|
||||
localparam ALUOP_SUB = 6'h01;
|
||||
localparam ALUOP_LT = 6'h02;
|
||||
localparam ALUOP_LTU = 6'h04;
|
||||
localparam ALUOP_AND = 6'h06;
|
||||
localparam ALUOP_OR = 6'h07;
|
||||
localparam ALUOP_XOR = 6'h08;
|
||||
localparam ALUOP_SRL = 6'h09;
|
||||
localparam ALUOP_SRA = 6'h0a;
|
||||
localparam ALUOP_SLL = 6'h0b;
|
||||
localparam ALUOP_MULDIV = 6'h0c;
|
||||
localparam ALUOP_RS2 = 6'h0d; // differs from AND/OR/XOR in [1:0]
|
||||
// Bitmanip ALU operations (some also used by AMOs):
|
||||
localparam ALUOP_SHXADD = 6'h20;
|
||||
localparam ALUOP_CLZ = 6'h23;
|
||||
localparam ALUOP_CPOP = 6'h24;
|
||||
localparam ALUOP_CTZ = 6'h25;
|
||||
localparam ALUOP_ANDN = 6'h26; // Same LSBs as non-inverted
|
||||
localparam ALUOP_ORN = 6'h27; // Same LSBs as non-inverted
|
||||
localparam ALUOP_XNOR = 6'h28; // Same LSBs as non-inverted
|
||||
localparam ALUOP_MAX = 6'h29;
|
||||
localparam ALUOP_MAXU = 6'h2a;
|
||||
localparam ALUOP_MIN = 6'h2b;
|
||||
localparam ALUOP_MINU = 6'h2c;
|
||||
localparam ALUOP_ORC_B = 6'h2d;
|
||||
localparam ALUOP_REV8 = 6'h2e;
|
||||
localparam ALUOP_ROL = 6'h2f;
|
||||
localparam ALUOP_ROR = 6'h30;
|
||||
localparam ALUOP_SEXT_B = 6'h31;
|
||||
localparam ALUOP_SEXT_H = 6'h32;
|
||||
localparam ALUOP_ZEXT_H = 6'h33;
|
||||
|
||||
localparam ALUOP_CLMUL = 6'h34;
|
||||
|
||||
localparam ALUOP_BCLR = 6'h35;
|
||||
localparam ALUOP_BEXT = 6'h36;
|
||||
localparam ALUOP_BINV = 6'h37;
|
||||
localparam ALUOP_BSET = 6'h38;
|
||||
|
||||
localparam ALUOP_PACK = 6'h39;
|
||||
localparam ALUOP_PACKH = 6'h3a;
|
||||
localparam ALUOP_BREV8 = 6'h3b;
|
||||
localparam ALUOP_ZIP = 6'h3c;
|
||||
localparam ALUOP_UNZIP = 6'h3d;
|
||||
|
||||
localparam ALUOP_BEXTM = 6'h3e;
|
||||
|
||||
localparam ALUOP_XPERM = 6'h3f;
|
||||
|
||||
// Parameters to control ALU input muxes. Bypass mux paths are
|
||||
// controlled by X, so D has no parameters to choose these.
|
||||
|
||||
localparam ALUSRCA_RS1 = 1'h0;
|
||||
localparam ALUSRCA_PC = 1'h1;
|
||||
|
||||
localparam ALUSRCB_RS2 = 1'h0;
|
||||
localparam ALUSRCB_IMM = 1'h1;
|
||||
|
||||
localparam MEMOP_LW = 5'h00;
|
||||
localparam MEMOP_LH = 5'h01;
|
||||
localparam MEMOP_LB = 5'h02;
|
||||
localparam MEMOP_LHU = 5'h03;
|
||||
localparam MEMOP_LBU = 5'h04;
|
||||
localparam MEMOP_SW = 5'h05;
|
||||
localparam MEMOP_SH = 5'h06;
|
||||
localparam MEMOP_SB = 5'h07;
|
||||
|
||||
localparam MEMOP_LR_W = 5'h08;
|
||||
localparam MEMOP_SC_W = 5'h09;
|
||||
localparam MEMOP_AMO = 5'h0a;
|
||||
localparam MEMOP_NONE = 5'h10;
|
||||
|
||||
localparam BCOND_NEVER = 2'h0;
|
||||
localparam BCOND_ALWAYS = 2'h1;
|
||||
localparam BCOND_ZERO = 2'h2;
|
||||
localparam BCOND_NZERO = 2'h3;
|
||||
|
||||
// CSR access types
|
||||
|
||||
localparam CSR_WTYPE_W = 2'h0;
|
||||
localparam CSR_WTYPE_S = 2'h1;
|
||||
localparam CSR_WTYPE_C = 2'h2;
|
||||
|
||||
// Exceptional condition signals which travel alongside (or instead of)
|
||||
// instructions in the pipeline. These are speculative and can be flushed on
|
||||
// e.g. branch mispredict. These mostly align with mcause values.
|
||||
|
||||
localparam EXCEPT_NONE = 4'hf;
|
||||
|
||||
localparam EXCEPT_INSTR_MISALIGN = 4'h0;
|
||||
localparam EXCEPT_INSTR_FAULT = 4'h1;
|
||||
localparam EXCEPT_INSTR_ILLEGAL = 4'h2;
|
||||
localparam EXCEPT_EBREAK = 4'h3;
|
||||
localparam EXCEPT_LOAD_ALIGN = 4'h4;
|
||||
localparam EXCEPT_LOAD_FAULT = 4'h5;
|
||||
localparam EXCEPT_STORE_ALIGN = 4'h6;
|
||||
localparam EXCEPT_STORE_FAULT = 4'h7;
|
||||
localparam EXCEPT_ECALL_U = 4'h8;
|
||||
// MRET, Return from M-mode: not really an exception, but handled like one
|
||||
localparam EXCEPT_MRET = 4'ha;
|
||||
localparam EXCEPT_ECALL_M = 4'hb;
|
||||
// spare: c
|
||||
// spare: d
|
||||
// REFETCH: flush and refetch sequentially-following instructions, e.g. on
|
||||
// executing fence.i. Jumps from stage 3 to get ordering against L/S dphase.
|
||||
localparam EXCEPT_REFETCH = 4'he;
|
||||
|
||||
// Operations for M extension (these are just instr[14:12])
|
||||
|
||||
localparam M_OP_MUL = 3'h0;
|
||||
localparam M_OP_MULH = 3'h1;
|
||||
localparam M_OP_MULHSU = 3'h2;
|
||||
localparam M_OP_MULHU = 3'h3;
|
||||
localparam M_OP_DIV = 3'h4;
|
||||
localparam M_OP_DIVU = 3'h5;
|
||||
localparam M_OP_REM = 3'h6;
|
||||
localparam M_OP_REMU = 3'h7;
|
||||
Vendored
+380
@@ -0,0 +1,380 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
// Physical memory protection unit
|
||||
|
||||
module hazard3_pmp #(
|
||||
`include "hazard3_config.vh"
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
|
||||
// Config interface passed through CSR block
|
||||
input wire [11:0] cfg_addr,
|
||||
input wire cfg_wen,
|
||||
input wire [W_DATA-1:0] cfg_wdata,
|
||||
output reg [W_DATA-1:0] cfg_rdata,
|
||||
|
||||
// Fetch address query
|
||||
input wire [W_ADDR-1:0] i_addr,
|
||||
input wire i_m_mode,
|
||||
output wire i_kill,
|
||||
|
||||
// Load/store address query
|
||||
input wire [W_ADDR-1:0] d_addr,
|
||||
// Broken out separately for carry-save:
|
||||
input wire [W_ADDR-1:0] d_addr_addend_rs1,
|
||||
input wire [W_ADDR-1:0] d_addr_addend_imm,
|
||||
input wire [W_ADDR-1:0] d_addr_addend_lspair_offs,
|
||||
input wire d_m_mode,
|
||||
input wire d_write,
|
||||
output wire d_kill
|
||||
);
|
||||
|
||||
localparam PMP_A_NAPOT = 2'b11;
|
||||
localparam PMP_A_NA4 = 2'b10;
|
||||
localparam PMP_A_TOR = 2'b01;
|
||||
localparam PMP_A_OFF = 2'b00;
|
||||
|
||||
// Which values are supported in A field (unsupported are mapped to OFF):
|
||||
localparam [3:0] PMP_A_SUPPORTED = {
|
||||
|PMP_MATCH_NAPOT,
|
||||
|PMP_MATCH_NAPOT && PMP_GRAIN == 0,
|
||||
|PMP_MATCH_TOR,
|
||||
1'b1
|
||||
};
|
||||
|
||||
`include "hazard3_csr_addr.vh"
|
||||
|
||||
generate
|
||||
if (PMP_REGIONS == 0) begin: no_pmp
|
||||
|
||||
// This should already be stubbed out in core.v, but use a generate here too
|
||||
// so that we don't get a warning for elaborating this module with a region
|
||||
// count of 0.
|
||||
|
||||
always @ (*) cfg_rdata = {W_DATA{1'b0}};
|
||||
assign i_kill = 1'b0;
|
||||
assign d_kill = 1'b0;
|
||||
|
||||
end else begin: have_pmp
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Config registers and read/write interface
|
||||
|
||||
// Whether a region's configuration is writable; this is non-trivial when TOR
|
||||
// is supported because locking region i + 1 can also lock region i.
|
||||
wire [PMP_REGIONS-1:0] region_locked;
|
||||
|
||||
reg [PMP_REGIONS-1:0] pmpcfg_l;
|
||||
reg [1:0] pmpcfg_a [0:PMP_REGIONS-1];
|
||||
reg [PMP_REGIONS-1:0] pmpcfg_x;
|
||||
reg [PMP_REGIONS-1:0] pmpcfg_w;
|
||||
reg [PMP_REGIONS-1:0] pmpcfg_r;
|
||||
|
||||
// Address register contains bits 33:2 of the address (to support 16 GiB
|
||||
// physical address space). We don't implement bits 33 or 32.
|
||||
reg [W_ADDR-3:0] pmpaddr [0:PMP_REGIONS-1];
|
||||
|
||||
// Hazard3 extension for applying PMP regions to M-mode without locking.
|
||||
// Different from ePMP mseccfg.rlb: low-numbered regions may be locked for
|
||||
// security reasons, but higher-numbered regions should stll be available for
|
||||
// other purposes e.g. stack guarding, peripheral emulation
|
||||
reg [PMP_REGIONS-1:0] pmpcfg_m;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin: cfg_update
|
||||
reg signed [31:0] i;
|
||||
if (!rst_n) begin
|
||||
for (i = 0; i < PMP_REGIONS; i = i + 1) begin
|
||||
pmpcfg_l[i] <= PMP_HARDWIRED[i] ? PMP_HARDWIRED_CFG[8 * i + 7] : 1'b0;
|
||||
pmpcfg_a[i] <= PMP_HARDWIRED[i] ? PMP_HARDWIRED_CFG[8 * i + 3 +: 2] : 2'h0;
|
||||
pmpcfg_x[i] <= PMP_HARDWIRED[i] ? PMP_HARDWIRED_CFG[8 * i + 2] : 1'b0;
|
||||
pmpcfg_w[i] <= PMP_HARDWIRED[i] ? PMP_HARDWIRED_CFG[8 * i + 1] : 1'b0;
|
||||
pmpcfg_r[i] <= PMP_HARDWIRED[i] ? PMP_HARDWIRED_CFG[8 * i + 0] : 1'b0;
|
||||
|
||||
pmpaddr[i] <= PMP_HARDWIRED[i] ? PMP_HARDWIRED_ADDR[32 * i +: 30] :
|
||||
PMP_GRAIN > 1 ? ~(~30'h0 << (PMP_GRAIN - 1)) : 30'h0;
|
||||
end
|
||||
pmpcfg_m <= {PMP_REGIONS{1'b0}};
|
||||
end else if (cfg_wen) begin
|
||||
for (i = 0; i < PMP_REGIONS; i = i + 1) begin
|
||||
if (cfg_addr == PMPCFG0 + i[13:2] && !region_locked[i]) begin
|
||||
if (PMP_HARDWIRED[i]) begin
|
||||
// Keep tied to hardwired value (but still make the "register" sensitive to clk)
|
||||
pmpcfg_l[i] <= PMP_HARDWIRED_CFG[8 * i + 7];
|
||||
pmpcfg_a[i] <= PMP_HARDWIRED_CFG[8 * i + 3 +: 2];
|
||||
pmpcfg_x[i] <= PMP_HARDWIRED_CFG[8 * i + 2];
|
||||
pmpcfg_w[i] <= PMP_HARDWIRED_CFG[8 * i + 1];
|
||||
pmpcfg_r[i] <= PMP_HARDWIRED_CFG[8 * i + 0];
|
||||
pmpaddr[i] <= PMP_HARDWIRED_ADDR[32 * i +: 30];
|
||||
end else begin
|
||||
pmpcfg_l[i] <= cfg_wdata[i % 4 * 8 + 7];
|
||||
pmpcfg_x[i] <= cfg_wdata[i % 4 * 8 + 2];
|
||||
pmpcfg_w[i] <= cfg_wdata[i % 4 * 8 + 1];
|
||||
pmpcfg_r[i] <= cfg_wdata[i % 4 * 8 + 0];
|
||||
// Unsupported A values are mapped to OFF (it's a WARL field).
|
||||
pmpcfg_a[i] <= PMP_A_SUPPORTED[cfg_wdata[i % 4 * 8 + 3 +: 2]] ?
|
||||
cfg_wdata[i % 4 * 8 + 3 +: 2] : PMP_A_OFF;
|
||||
end
|
||||
end
|
||||
if (cfg_addr == PMPADDR0 + i[11:0] && !region_locked[i]) begin
|
||||
// This implements one bit too many when G > 0 and only
|
||||
// PMP_MATCH_TOR is enabled, however that bit is ignored for
|
||||
// both rdata and address matching, so should be trimmed.
|
||||
if (PMP_GRAIN > 1) begin
|
||||
pmpaddr[i] <= cfg_wdata[W_ADDR-3:0] | ~(~30'h0 << (PMP_GRAIN - 1));
|
||||
end else begin
|
||||
pmpaddr[i] <= cfg_wdata[W_ADDR-3:0];
|
||||
end
|
||||
end
|
||||
end
|
||||
if (cfg_addr == PMPCFGM0) begin
|
||||
pmpcfg_m <= cfg_wdata[PMP_REGIONS-1:0] & ~PMP_HARDWIRED & {PMP_REGIONS{|EXTENSION_XH3PMPM}};
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
always @ (*) begin: cfg_read
|
||||
reg signed [31:0] i;
|
||||
cfg_rdata = {W_DATA{1'b0}};
|
||||
for (i = 0; i < PMP_REGIONS; i = i + 1) begin
|
||||
if (cfg_addr == PMPCFG0 + i[13:2]) begin
|
||||
cfg_rdata[i % 4 * 8 +: 8] = {
|
||||
pmpcfg_l[i],
|
||||
2'b00,
|
||||
pmpcfg_a[i],
|
||||
pmpcfg_x[i],
|
||||
pmpcfg_w[i],
|
||||
pmpcfg_r[i]
|
||||
};
|
||||
end else if (cfg_addr == PMPADDR0 + i[11:0]) begin
|
||||
if (PMP_GRAIN >= 2 && pmpcfg_a[i][1]) begin
|
||||
// Bits G-2:0 read back as all-ones when A is NA4 or NAPOT.
|
||||
cfg_rdata[W_ADDR-3:0] = pmpaddr[i] | ~({W_ADDR-2{1'b1}} << (PMP_GRAIN - 1));
|
||||
end else if (PMP_GRAIN >= 1 && !pmpcfg_a[i][1]) begin
|
||||
// Bits G-1:0 read back as all-zeroes when A is OFF or TOR.
|
||||
cfg_rdata[W_ADDR-3:0] = pmpaddr[i] & ({W_ADDR-2{1'b1}} << PMP_GRAIN);
|
||||
end else begin
|
||||
cfg_rdata[W_ADDR-3:0] = pmpaddr[i];
|
||||
end
|
||||
end
|
||||
end
|
||||
if (cfg_addr == PMPCFGM0) begin
|
||||
cfg_rdata = {{32-PMP_REGIONS{1'b0}}, pmpcfg_m} & {32{|EXTENSION_XH3PMPM}};
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Region locking rules
|
||||
|
||||
reg [PMP_REGIONS-1:0] pmp_region_is_tor;
|
||||
always @ (*) begin: check_region_is_tor
|
||||
integer i;
|
||||
for (i = 0; i < PMP_REGIONS; i = i + 1) begin
|
||||
pmp_region_is_tor[i] = PMP_MATCH_TOR && pmpcfg_a[i] == PMP_A_TOR;
|
||||
end
|
||||
end
|
||||
|
||||
assign region_locked = pmpcfg_l | ((pmpcfg_l & pmp_region_is_tor) >> 1);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Match addresses against regions
|
||||
|
||||
wire [PMP_REGIONS-1:0] d_match_napot;
|
||||
wire [PMP_REGIONS-1:0] i_match_napot;
|
||||
wire [PMP_REGIONS-1:0] d_match_tor;
|
||||
wire [PMP_REGIONS-1:0] i_match_tor;
|
||||
|
||||
if (PMP_MATCH_NAPOT != 0) begin: have_napot
|
||||
|
||||
reg [PMP_REGIONS-1:0] d_match_napot_r;
|
||||
reg [PMP_REGIONS-1:0] i_match_napot_r;
|
||||
|
||||
assign d_match_napot = d_match_napot_r;
|
||||
assign i_match_napot = i_match_napot_r;
|
||||
|
||||
// Decode PMPCFGx.A and PMPADDRx into a 32-bit address mask and address
|
||||
reg [W_ADDR-1:0] match_mask [0:PMP_REGIONS-1];
|
||||
reg [W_ADDR-1:0] match_addr [0:PMP_REGIONS-1];
|
||||
|
||||
// Encoding: (noting ADDR is a 4-byte address, not a word address):
|
||||
// CFG.A | ADDR | Region size
|
||||
// ------+----------+------------
|
||||
// NA4 | y..yyyyy | 4 bytes
|
||||
// NAPOT | y..yyyy0 | 8 bytes
|
||||
// NAPOT | y..yyy01 | 16 bytes
|
||||
// NAPOT | y..yy011 | 32 bytes
|
||||
// NAPOT | y..y0111 | 64 bytes
|
||||
// etc.
|
||||
//
|
||||
// So, with the exception of NA4, the rule is to check all bits more
|
||||
// significant than the least-significant 0 bit.
|
||||
|
||||
always @ (*) begin: decode_match_mask_addr
|
||||
integer i, j;
|
||||
for (i = 0; i < PMP_REGIONS; i = i + 1) begin
|
||||
if (!pmpcfg_a[i][0]) begin
|
||||
match_mask[i] = {{W_ADDR-2{1'b1}}, 2'b00};
|
||||
end else begin
|
||||
// Bits 1:0 are always 0. Bit 2 is 0 because NAPOT is at least 8 bytes.
|
||||
match_mask[i] = {W_ADDR{1'b0}};
|
||||
for (j = 3; j < W_ADDR; j = j + 1) begin
|
||||
match_mask[i][j] = match_mask[i][j - 1] || !pmpaddr[i][j - 3];
|
||||
end
|
||||
end
|
||||
match_addr[i] = {pmpaddr[i], 2'b00} & match_mask[i];
|
||||
end
|
||||
end
|
||||
|
||||
// We check only the least-addressed byte of each access. See later
|
||||
// comments for an argument as to why this is sufficient.
|
||||
|
||||
always @ (*) begin: check_d_match
|
||||
integer i;
|
||||
for (i = PMP_REGIONS - 1; i >= 0; i = i - 1) begin
|
||||
d_match_napot_r[i] = pmpcfg_a[i][1] &&
|
||||
(d_addr & match_mask[i]) == match_addr[i];
|
||||
i_match_napot_r[i] = pmpcfg_a[i][1] &&
|
||||
(i_addr & match_mask[i]) == match_addr[i];
|
||||
end
|
||||
end
|
||||
|
||||
end else begin: no_napot
|
||||
|
||||
assign d_match_napot = {PMP_REGIONS{1'b0}};
|
||||
assign i_match_napot = {PMP_REGIONS{1'b0}};
|
||||
|
||||
end
|
||||
|
||||
if (PMP_MATCH_TOR != 0) begin: have_tor
|
||||
|
||||
reg [PMP_REGIONS-1:0] d_match_tor_r;
|
||||
reg [PMP_REGIONS-1:0] i_match_tor_r;
|
||||
reg [W_ADDR-1:0] watermark [0:PMP_REGIONS-1];
|
||||
reg [PMP_REGIONS-1:0] d_lt;
|
||||
reg [PMP_REGIONS-1:0] i_lt;
|
||||
|
||||
assign d_match_tor = d_match_tor_r;
|
||||
assign i_match_tor = i_match_tor_r;
|
||||
|
||||
always @ (*) begin: compare
|
||||
integer i;
|
||||
for (i = 0; i < PMP_REGIONS; i = i + 1) begin
|
||||
watermark[i] = {
|
||||
pmpaddr[i][W_ADDR-3:0] & (~30'h0 << PMP_GRAIN),
|
||||
2'b00
|
||||
};
|
||||
// Bring terms in separately to try to encourage adder merging
|
||||
d_lt[i] = (
|
||||
d_addr_addend_rs1 + d_addr_addend_imm + d_addr_addend_lspair_offs
|
||||
) < watermark[i];
|
||||
i_lt[i] = i_addr < watermark[i];
|
||||
end
|
||||
end
|
||||
|
||||
wire [PMP_REGIONS-1:0] d_prev_ge = ~(d_lt << 1);
|
||||
wire [PMP_REGIONS-1:0] i_prev_ge = ~(i_lt << 1);
|
||||
|
||||
always @ (*) begin: match
|
||||
integer i;
|
||||
for (i = 0; i < PMP_REGIONS; i = i + 1) begin
|
||||
d_match_tor_r[i] = d_lt[i] && d_prev_ge[i] && pmpcfg_a[i] == PMP_A_TOR;
|
||||
i_match_tor_r[i] = i_lt[i] && i_prev_ge[i] && pmpcfg_a[i] == PMP_A_TOR;
|
||||
end
|
||||
end
|
||||
|
||||
end else begin: no_tor
|
||||
|
||||
assign d_match_tor = {PMP_REGIONS{1'b0}};
|
||||
assign i_match_tor = {PMP_REGIONS{1'b0}};
|
||||
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Decode permissions from matches
|
||||
|
||||
// For load/stores we assume any non-naturally-aligned transfers trigger a
|
||||
// misaligned load/store/AMO exception, so we only need to decode the PMP
|
||||
// attribute for the first byte of the access. Note the spec gives us freedom
|
||||
// to report *either* a load/store/AMO access fault (mcause = 5, 7) or a
|
||||
// load/store/AMO alignment fault (mcause = 4, 6), in the case that both
|
||||
// happen, and we choose alignment fault in this case.
|
||||
|
||||
reg d_m; // Hazard3 extension (M-mode without locking)
|
||||
reg d_l;
|
||||
reg d_r;
|
||||
reg d_w;
|
||||
|
||||
always @ (*) begin: check_d_match
|
||||
integer i;
|
||||
d_m = 1'b0;
|
||||
d_l = 1'b0;
|
||||
d_r = 1'b0;
|
||||
d_w = 1'b0;
|
||||
// Lowest-numbered match wins, so work down from the top. This should be
|
||||
// inferred as a priority mux structure (cascade mux).
|
||||
for (i = PMP_REGIONS - 1; i >= 0; i = i - 1) begin
|
||||
if (d_match_napot[i] || d_match_tor[i]) begin
|
||||
d_m = pmpcfg_m[i];
|
||||
d_l = pmpcfg_l[i];
|
||||
d_r = pmpcfg_r[i];
|
||||
d_w = pmpcfg_w[i];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Instructions work similarly because we check *fetches*, not instructions.
|
||||
// Fetch is always word-sized word-aligned. The spec permits this:
|
||||
//
|
||||
// "On some implementations, misaligned loads, stores, and instruction fetches
|
||||
// may also be decomposed into multiple accesses, some of which may succeed
|
||||
// before an access-fault exception occurs."
|
||||
//
|
||||
// Hazard3 separately checks the naturally-aligned fetches that occur in the
|
||||
// course of fetching a non-naturally-aligned instruction. This means
|
||||
// instruction fetch spanning two different regions which both grant X
|
||||
// permission *is* permitted, unlike the RP2350 version of Hazard3.
|
||||
|
||||
reg i_m; // Hazard3 extension (M-mode without locking)
|
||||
reg i_l;
|
||||
reg i_x;
|
||||
|
||||
always @ (*) begin: check_i_match
|
||||
integer i;
|
||||
i_m = 1'b0;
|
||||
i_l = 1'b0;
|
||||
i_x = 1'b0;
|
||||
for (i = PMP_REGIONS - 1; i >= 0; i = i - 1) begin
|
||||
if (i_match_napot[i] || i_match_tor[i]) begin
|
||||
i_m = pmpcfg_m[i];
|
||||
i_l = pmpcfg_l[i];
|
||||
i_x = pmpcfg_x[i];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Access rules
|
||||
|
||||
// M-mode gets to ignore protections, unless the lock or M-mode bit is set.
|
||||
|
||||
assign d_kill = (!d_m_mode || d_l || d_m) && (
|
||||
(!d_write && !d_r) ||
|
||||
( d_write && !d_w)
|
||||
);
|
||||
|
||||
assign i_kill = (!i_m_mode || i_l || i_m) && !i_x;
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
// Wake/sleep (power) state machine for Hazard3
|
||||
|
||||
module hazard3_power_ctrl #(
|
||||
`include "hazard3_config.vh"
|
||||
) (
|
||||
input wire clk_always_on,
|
||||
input wire rst_n,
|
||||
|
||||
// 4-phase (Gray code) req/ack handshake for requesting and releasing
|
||||
// power+clock enable on non-processor hardware, e.g. the bus fabric. This
|
||||
// can also be used for an external controller to gate the processor's clk
|
||||
// input, rather than the clk_en signal below.
|
||||
output reg pwrup_req,
|
||||
input wire pwrup_ack,
|
||||
|
||||
// Top-level clock enable for an optional clock gate on the processor's clk
|
||||
// input (but not clk_always_on, which clocks this module and the IRQ input
|
||||
// flops). This allows the processor to clock-gate when sleeping. It's
|
||||
// acceptable for the clock gate cell to have one cycle of delay when
|
||||
// clk_en changes.
|
||||
output reg clk_en,
|
||||
|
||||
// Power state controls from CSRs
|
||||
input wire allow_clkgate,
|
||||
input wire allow_power_down,
|
||||
input wire allow_sleep_on_block,
|
||||
|
||||
// Signal from frontend that it has stalled against the WFI pipeline
|
||||
// stall, and we are now clear to enter a deep sleep state
|
||||
input wire frontend_pwrdown_ok,
|
||||
|
||||
input wire sleeping_on_wfi,
|
||||
input wire wfi_wakeup_req,
|
||||
input wire sleeping_on_block,
|
||||
input wire block_wakeup_req_pulse,
|
||||
output reg stall_release
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Wake/sleep state machine
|
||||
|
||||
localparam W_STATE = 2;
|
||||
localparam S_AWAKE = 2'h0;
|
||||
localparam S_ENTER_ASLEEP = 2'h1;
|
||||
localparam S_ASLEEP = 2'h2;
|
||||
localparam S_ENTER_AWAKE = 2'h3;
|
||||
|
||||
reg [W_STATE-1:0] state;
|
||||
reg block_wakeup_req;
|
||||
|
||||
wire active_wake_req =
|
||||
(sleeping_on_block && (block_wakeup_req || wfi_wakeup_req)) ||
|
||||
(sleeping_on_wfi && wfi_wakeup_req);
|
||||
|
||||
// Note: we assert our power up request during reset, and *assume* that the
|
||||
// power up acknowledge is also high at reset. If this is a problem, extend
|
||||
// the core reset.
|
||||
|
||||
always @ (posedge clk_always_on or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
state <= S_AWAKE;
|
||||
pwrup_req <= 1'b1;
|
||||
clk_en <= 1'b1;
|
||||
stall_release <= 1'b0;
|
||||
end else begin
|
||||
stall_release <= 1'b0;
|
||||
case (state)
|
||||
S_AWAKE: if (sleeping_on_wfi || sleeping_on_block) begin
|
||||
if (stall_release) begin
|
||||
// The last cycle of an ongoing which we have just released. Sit
|
||||
// tight, this instruction will move down the pipeline at the
|
||||
// end of this cycle. (There is an assertion that this doesn't
|
||||
// happen twice.)
|
||||
state <= S_AWAKE;
|
||||
end else if (active_wake_req) begin
|
||||
// Skip deep sleep if it would immediately fall through.
|
||||
stall_release <= 1'b1;
|
||||
end else if ((allow_power_down || allow_clkgate) && (sleeping_on_wfi || allow_sleep_on_block)) begin
|
||||
if (frontend_pwrdown_ok) begin
|
||||
pwrup_req <= !allow_power_down;
|
||||
clk_en <= !allow_clkgate;
|
||||
state <= allow_power_down ? S_ENTER_ASLEEP : S_ASLEEP;
|
||||
end else begin
|
||||
// Stay awake until it is safe to power down (i.e. until our
|
||||
// instruction fetch goes quiet).
|
||||
state <= S_AWAKE;
|
||||
end
|
||||
end else begin
|
||||
// No power state change. Just sit with the pipeline stalled.
|
||||
state <= S_AWAKE;
|
||||
end
|
||||
end
|
||||
S_ENTER_ASLEEP: if (!pwrup_ack) begin
|
||||
state <= S_ASLEEP;
|
||||
end
|
||||
S_ASLEEP: if (active_wake_req) begin
|
||||
pwrup_req <= 1'b1;
|
||||
clk_en <= 1'b1;
|
||||
// Still go through the enter state for non-power-down wakeup, in
|
||||
// case the clock gate cell has a 1 cycle delay.
|
||||
state <= S_ENTER_AWAKE;
|
||||
end
|
||||
S_ENTER_AWAKE: if (pwrup_ack || !allow_power_down) begin
|
||||
state <= S_AWAKE;
|
||||
stall_release <= 1'b1;
|
||||
end
|
||||
default: begin
|
||||
state <= S_AWAKE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
// Regs are a workaround for the non-constant reset value issue with
|
||||
// $past() in yosys-smtbmc.
|
||||
reg past_sleeping;
|
||||
reg past_stall_release;
|
||||
always @ (posedge clk_always_on) begin
|
||||
if (!rst_n) begin
|
||||
past_sleeping <= 1'b0;
|
||||
past_stall_release <= 1'b0;
|
||||
end else begin
|
||||
past_sleeping <= sleeping_on_wfi || sleeping_on_block;
|
||||
past_stall_release <= stall_release;
|
||||
// These must always be mutually exclusive.
|
||||
assert(!(sleeping_on_wfi && sleeping_on_block));
|
||||
if (stall_release) begin
|
||||
// Presumably there was a stall which we just released
|
||||
assert(past_sleeping);
|
||||
// Presumably we are still in that stall
|
||||
assert(sleeping_on_wfi|| sleeping_on_block);
|
||||
// It takes one cycle to do a release and enter a new sleep state, so a
|
||||
// double release should be impossible.
|
||||
assert(!past_stall_release);
|
||||
end
|
||||
if (state == S_ASLEEP) begin
|
||||
assert(allow_power_down || allow_clkgate);
|
||||
end
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Pulse->level for block wakeup
|
||||
|
||||
// Unblock signal is sticky: a prior unblock with no block since will cause
|
||||
// the next block to immediately fall through.
|
||||
|
||||
always @ (posedge clk_always_on or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
block_wakeup_req <= 1'b0;
|
||||
end else begin
|
||||
// Note the OR takes precedence over the AND, so we don't miss a second
|
||||
// unblock that arrives at the instant we wake up.
|
||||
block_wakeup_req <= (block_wakeup_req && !(
|
||||
sleeping_on_block && stall_release
|
||||
)) || block_wakeup_req_pulse;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Register file
|
||||
// Single write port, dual read port
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_regfile_1w2r #(
|
||||
`include "hazard3_config.vh"
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
|
||||
input wire [4:0] raddr1,
|
||||
output reg [W_DATA-1:0] rdata1,
|
||||
|
||||
input wire [4:0] raddr2,
|
||||
output reg [W_DATA-1:0] rdata2,
|
||||
|
||||
input wire [4:0] waddr,
|
||||
input wire [W_DATA-1:0] wdata,
|
||||
input wire wen
|
||||
);
|
||||
|
||||
localparam N_REGS = EXTENSION_E == 0 ? 32 : 16;
|
||||
localparam [4:0] REGNUM_MASK = {~|EXTENSION_E, 4'hf};
|
||||
|
||||
wire [4:0] raddr1_masked = raddr1 & REGNUM_MASK;
|
||||
wire [4:0] raddr2_masked = raddr2 & REGNUM_MASK;
|
||||
wire [4:0] waddr_masked = waddr & REGNUM_MASK;
|
||||
|
||||
generate
|
||||
if (RESET_REGFILE) begin: real_dualport_reset
|
||||
// This will presumably always be implemented with flops
|
||||
reg [W_DATA-1:0] mem [0:N_REGS-1];
|
||||
|
||||
integer i;
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
for (i = 0; i < N_REGS; i = i + 1) begin
|
||||
mem[i] <= {W_DATA{1'b0}};
|
||||
end
|
||||
rdata1 <= {W_DATA{1'b0}};
|
||||
rdata2 <= {W_DATA{1'b0}};
|
||||
end else begin
|
||||
if (wen) begin
|
||||
mem[waddr_masked] <= wdata;
|
||||
end
|
||||
rdata1 <= mem[raddr1_masked];
|
||||
rdata2 <= mem[raddr2_masked];
|
||||
end
|
||||
end
|
||||
end else begin: real_dualport_noreset
|
||||
// This should be inference-compatible on FPGAs with dual-port (or 1R1W) BRAMs
|
||||
`ifdef YOSYS
|
||||
`ifdef FPGA_ICE40
|
||||
// We do not require write-to-read bypass logic on the BRAM
|
||||
(* no_rw_check *)
|
||||
`endif
|
||||
`endif
|
||||
// Optionally force use of distributed RAM on Xilinx for better timing
|
||||
`ifdef HAZARD3_REGFILE_RAM_STYLE_DISTRIBUTED
|
||||
(* ram_style = "distributed" *)
|
||||
`endif
|
||||
reg [W_DATA-1:0] mem [0:N_REGS-1];
|
||||
|
||||
always @ (posedge clk) begin
|
||||
if (wen) begin
|
||||
mem[waddr_masked] <= wdata;
|
||||
end
|
||||
rdata1 <= mem[raddr1_masked];
|
||||
rdata2 <= mem[raddr2_masked];
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+377
@@ -0,0 +1,377 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2025 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RVFI Instrumentation
|
||||
// ----------------------------------------------------------------------------
|
||||
// To be included into hazard3_core.v for use with riscv-formal.
|
||||
// Contains some state modelling to diagnose exactly what the core is doing,
|
||||
// and report this in a way RVFI understands.
|
||||
// We consider instructions to "retire" as they cross the M/W pipe register.
|
||||
//
|
||||
// All modelling signals prefixed with rvfm (riscv-formal monitor)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Instruction monitor
|
||||
|
||||
// Diagnose whether X, M contain valid in-flight instructions, to produce
|
||||
// rvfi_valid signal.
|
||||
|
||||
wire rvfm_x_valid = fd_cir_vld >= 2 || (fd_cir_vld >= 1 && fd_cir_raw[1:0] != 2'b11);
|
||||
|
||||
reg rvfm_m_valid;
|
||||
reg [31:0] rvfm_m_instr;
|
||||
|
||||
wire rvfm_m_trap = xm_except != EXCEPT_NONE && xm_except != EXCEPT_MRET && m_trap_enter_rdy;
|
||||
|
||||
reg rvfi_valid_r;
|
||||
reg [31:0] rvfi_insn_r;
|
||||
reg rvfi_trap_r;
|
||||
|
||||
assign rvfi_valid = rvfi_valid_r;
|
||||
assign rvfi_insn = rvfi_insn_r;
|
||||
assign rvfi_trap = rvfi_trap_r;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
rvfm_m_valid <= 1'b0;
|
||||
rvfi_valid_r <= 1'b0;
|
||||
rvfi_trap_r <= 1'b0;
|
||||
rvfi_insn_r <= 32'h0;
|
||||
end else begin
|
||||
if (!x_stall) begin
|
||||
// X instruction squashed by any trap, as it's in the branch
|
||||
// shadow.
|
||||
rvfm_m_valid <= |df_cir_use && !m_trap_enter_vld;
|
||||
rvfm_m_instr <= {fd_cir_raw[31:16] & {16{df_cir_use[1]}}, fd_cir_raw[15:0]};
|
||||
end else if (!m_stall) begin
|
||||
rvfm_m_valid <= 1'b0;
|
||||
end
|
||||
rvfi_valid_r <= rvfm_m_valid && !m_stall;
|
||||
// Instructions which experienced fetch faults are reported as
|
||||
// all-zeroes, per riscv-formal docs.
|
||||
rvfi_insn_r <= rvfm_m_instr & {32{
|
||||
xm_except != EXCEPT_INSTR_FAULT &&
|
||||
xm_except != EXCEPT_INSTR_MISALIGN
|
||||
}};
|
||||
rvfi_trap_r <= rvfm_m_trap;
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
always @ (posedge clk) if (rst_n) begin
|
||||
// Sanity checks for above
|
||||
if (d_rd != 5'h0)
|
||||
assert(rvfm_x_valid);
|
||||
if (xm_rd != 5'h0)
|
||||
assert(rvfm_m_valid);
|
||||
end
|
||||
`endif
|
||||
|
||||
// Track whether an instruction is the first of an interrupt or exception;
|
||||
// when a trap happens, a flag is installed in stage X, and once a new
|
||||
// instruction arrives the flag travels alongside it down to the RVFI port.
|
||||
reg rvfm_x_intr;
|
||||
reg rvfm_m_intr;
|
||||
reg rvfi_intr_r;
|
||||
always @ (posedge clk) begin
|
||||
if (!rst_n) begin
|
||||
rvfm_x_intr <= 1'b0;
|
||||
rvfm_m_intr <= 1'b0;
|
||||
rvfi_intr_r <= 1'b0;
|
||||
end else begin
|
||||
rvfm_x_intr <= (rvfm_x_intr && (x_stall || d_starved || fd_cir_uop_nonfinal || df_lspair_phase_next)) ||
|
||||
(m_trap_enter_vld && m_trap_enter_rdy);
|
||||
if (!x_stall) begin
|
||||
rvfm_m_intr <= rvfm_x_intr;
|
||||
end
|
||||
if (!m_stall) begin
|
||||
rvfi_intr_r <= rvfm_m_intr;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
// Hazard3 is an in-order core:
|
||||
reg [63:0] rvfm_retire_ctr;
|
||||
assign rvfi_order = rvfm_retire_ctr;
|
||||
always @ (posedge clk or negedge rst_n)
|
||||
if (!rst_n)
|
||||
rvfm_retire_ctr <= 0;
|
||||
else if (rvfi_valid)
|
||||
rvfm_retire_ctr <= rvfm_retire_ctr + 1;
|
||||
|
||||
assign rvfi_intr = rvfi_intr_r && rvfi_valid;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PC and jump monitor
|
||||
|
||||
reg [31:0] rvfm_xm_pc;
|
||||
reg [31:0] rvfm_xm_pc_next;
|
||||
|
||||
// Record a jump target that was issued while stalled
|
||||
reg rvfm_x_saw_f_jump;
|
||||
reg [31:0] rvfm_x_saw_f_jump_target;
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
rvfm_x_saw_f_jump <= 1'b0;
|
||||
rvfm_x_saw_f_jump_target <= 32'd0;
|
||||
end else if (f_jump_now && !(m_trap_enter_vld && m_trap_enter_rdy) && (x_stall || (fd_cir_is_uop && fd_cir_uop_nonfinal))) begin
|
||||
// Record fetch address issued by instruction. Note this case is gated
|
||||
// on m_trap_enter_vld && m_trap_enter_rdy (not just vld). If
|
||||
// !m_trap_enter_rdy it is still possible to get a new fetch address
|
||||
// in the following case:
|
||||
//
|
||||
// * M instr is a load/store (in dphase)
|
||||
//
|
||||
// * IRQ is asserted, but blocked by the load/store dphase to avoid
|
||||
// trashing exception PC of a potential data-phase bus fault
|
||||
//
|
||||
// * X instr is a jump, which stalls due to the IRQ assertion
|
||||
//
|
||||
// * X instr's PC goes through to frontend during stall (and would be
|
||||
// flushed if the IRQ went through) because stall cannot gate fetch
|
||||
// address request to avoid AHB through-path.
|
||||
//
|
||||
// * IRQ's trap address is not immediately accepted by frontend due to
|
||||
// address-phase stall on issuing jump instr's address
|
||||
//
|
||||
// * IRQ deasserts on the next cycle, so its trap address is not accepted.
|
||||
rvfm_x_saw_f_jump <= 1'b1;
|
||||
rvfm_x_saw_f_jump_target <= f_jump_target;
|
||||
end else if (!x_stall && !(fd_cir_is_uop && fd_cir_uop_nonfinal)) begin
|
||||
rvfm_x_saw_f_jump <= 1'b0;
|
||||
end else if (m_trap_enter_vld && m_trap_enter_rdy) begin
|
||||
// E.g. trap during uop sequence
|
||||
rvfm_x_saw_f_jump <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
rvfm_xm_pc <= 0;
|
||||
rvfm_xm_pc_next <= 0;
|
||||
end else begin
|
||||
if (!x_stall) begin
|
||||
// For cm.popret and cm.popretz the PC actually changes on the
|
||||
// penultimate uop; ignore this and retain the initial PC. Abuse
|
||||
// knowledge that the PC update is always in the atomic section,
|
||||
// and the first instruction is always interruptible.
|
||||
if (!(fd_cir_is_uop && fd_cir_uop_atomic)) begin
|
||||
rvfm_xm_pc <= d_pc;
|
||||
end
|
||||
rvfm_xm_pc_next <=
|
||||
f_jump_now ? f_jump_target :
|
||||
rvfm_x_saw_f_jump ? rvfm_x_saw_f_jump_target :
|
||||
d_pc + (fd_cir_raw[1:0] == 2'b11 ? 32'h4 : 32'h2);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
reg [31:0] rvfi_pc_rdata_r;
|
||||
reg [31:0] rvfi_pc_wdata_r;
|
||||
|
||||
assign rvfi_pc_rdata = rvfi_pc_rdata_r;
|
||||
assign rvfi_pc_wdata = rvfi_pc_wdata_r;
|
||||
|
||||
always @ (posedge clk) begin
|
||||
if (!m_stall) begin
|
||||
rvfi_pc_rdata_r <= rvfm_xm_pc;
|
||||
rvfi_pc_wdata_r <=
|
||||
m_trap_enter_vld && m_trap_enter_rdy && xm_except != EXCEPT_NONE ?
|
||||
m_trap_addr : rvfm_xm_pc_next;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Register file monitor:
|
||||
|
||||
// When writeback is suppressed due to trap, the previous instruction is left
|
||||
// in the writeback buffer (and can be re-bypassed from there). Make sure not
|
||||
// to report this as a writeback on RVFI.
|
||||
reg rvfm_writeback_mask;
|
||||
always @ (posedge clk) begin
|
||||
if (!m_stall) begin
|
||||
rvfm_writeback_mask <= m_reg_wen_if_nonzero;
|
||||
end
|
||||
end
|
||||
|
||||
assign rvfi_rd_addr = mw_rd & {5{rvfm_writeback_mask}};
|
||||
assign rvfi_rd_wdata = |mw_rd && rvfm_writeback_mask ? mw_result : 32'h0;
|
||||
|
||||
// Do not reimplement internal bypassing logic. Danger of implementing
|
||||
// it correctly here but incorrectly in core.
|
||||
|
||||
reg [31:0] rvfm_xm_rdata1;
|
||||
reg [31:0] rvfm_xm_rdata2;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
rvfm_xm_rdata1 <= 32'h0;
|
||||
rvfm_xm_rdata2 <= 32'h0;
|
||||
end else if (!x_stall) begin
|
||||
// rs*_bypass may have garbage on them for instructions with *no*
|
||||
// register operands (due to some interesting optimisations), though
|
||||
// d_rs* is still driven to 0 to disable stalling on that register
|
||||
// lane. riscv-formal still likes to see zeroes from x0, so fix that
|
||||
// up here. This shouldn't cover up any bugs, since a
|
||||
// register-operand instruction would still *use* the garbage value.
|
||||
rvfm_xm_rdata1 <= |d_rs1 ? x_rs1_bypass : 32'h0;
|
||||
rvfm_xm_rdata2 <= |d_rs2 ? x_rs2_bypass : 32'h0;
|
||||
end
|
||||
end
|
||||
|
||||
reg [4:0] rvfi_rs1_addr_r;
|
||||
reg [4:0] rvfi_rs2_addr_r;
|
||||
reg [31:0] rvfi_rs1_rdata_r;
|
||||
reg [31:0] rvfi_rs2_rdata_r;
|
||||
|
||||
assign rvfi_rs1_addr = rvfi_rs1_addr_r;
|
||||
assign rvfi_rs2_addr = rvfi_rs2_addr_r;
|
||||
assign rvfi_rs1_rdata = rvfi_rs1_rdata_r;
|
||||
assign rvfi_rs2_rdata = rvfi_rs2_rdata_r;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
rvfi_rs1_addr_r <= 5'h0;
|
||||
rvfi_rs2_addr_r <= 5'h0;
|
||||
rvfi_rs1_rdata_r <= 32'h0;
|
||||
rvfi_rs2_rdata_r <= 32'h0;
|
||||
end else begin
|
||||
rvfi_rs1_addr_r <= m_stall ? 5'h0 : xm_rs1;
|
||||
rvfi_rs2_addr_r <= m_stall ? 5'h0 : xm_rs2;
|
||||
rvfi_rs1_rdata_r <= rvfm_xm_rdata1;
|
||||
rvfi_rs2_rdata_r <= xm_rs2 == mw_rd && |xm_rs2 ? m_wdata : rvfm_xm_rdata2;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Load/store monitor: based on bus signals, NOT processor internals.
|
||||
// Marshal up a description of the current data phase, and then register this
|
||||
// into the RVFI signals.
|
||||
|
||||
`ifdef HAZARD3_ASSERTIONS
|
||||
`ifndef RISCV_FORMAL_ALIGNED_MEM
|
||||
initial $fatal;
|
||||
`endif
|
||||
`endif
|
||||
|
||||
reg [31:0] rvfm_haddr_dph;
|
||||
reg rvfm_hwrite_dph;
|
||||
reg [1:0] rvfm_htrans_dph;
|
||||
reg [2:0] rvfm_hsize_dph;
|
||||
|
||||
always @ (posedge clk) begin
|
||||
if (bus_aph_ready_d) begin
|
||||
rvfm_htrans_dph <= {bus_aph_req_d, 1'b0};
|
||||
rvfm_haddr_dph <= bus_haddr_d;
|
||||
rvfm_hwrite_dph <= bus_hwrite_d;
|
||||
rvfm_hsize_dph <= bus_hsize_d;
|
||||
end
|
||||
end
|
||||
|
||||
wire [3:0] rvfm_mem_bytemask_dph = (
|
||||
rvfm_hsize_dph == 3'h0 ? 4'h1 :
|
||||
rvfm_hsize_dph == 3'h1 ? 4'h3 :
|
||||
4'hf
|
||||
) << rvfm_haddr_dph[1:0];
|
||||
|
||||
reg [31:0] rvfi_mem_addr_r;
|
||||
reg [3:0] rvfi_mem_rmask_r;
|
||||
reg [31:0] rvfi_mem_rdata_r;
|
||||
reg [3:0] rvfi_mem_wmask_r;
|
||||
reg [31:0] rvfi_mem_wdata_r;
|
||||
reg rvfi_mem_fault_r;
|
||||
// May have to hold the strobes for multiple cycles following a bus
|
||||
// fault, as the trap entry may not go through immediately (depending
|
||||
// on instruction-side bus stall):
|
||||
reg rvfm_mem_hold;
|
||||
|
||||
assign rvfi_mem_addr = rvfi_mem_addr_r;
|
||||
assign rvfi_mem_rdata = rvfi_mem_rdata_r;
|
||||
assign rvfi_mem_wdata = rvfi_mem_wdata_r;
|
||||
|
||||
assign rvfi_mem_fault = rvfi_mem_fault_r;
|
||||
assign rvfi_mem_wmask = rvfi_mem_wmask_r & {4{!rvfi_mem_fault_r}};
|
||||
assign rvfi_mem_rmask = rvfi_mem_rmask_r & {4{!rvfi_mem_fault_r}};
|
||||
assign rvfi_mem_fault_rmask = rvfi_mem_rmask_r & {4{ rvfi_mem_fault_r}};
|
||||
assign rvfi_mem_fault_wmask = rvfi_mem_wmask_r & {4{ rvfi_mem_fault_r}};
|
||||
|
||||
always @ (posedge clk) begin
|
||||
rvfm_mem_hold <= (rvfm_mem_hold || (rvfm_htrans_dph && bus_dph_ready_d)) && m_stall;
|
||||
if (xm_memop == MEMOP_AMO) begin
|
||||
// AMO has completed in stage X. Progressing to stage M without MEMOP
|
||||
// going to NONE then there has been no trap, therefore no stall,
|
||||
// therefore no time for another address to have issued:
|
||||
assert(!m_stall);
|
||||
rvfi_mem_addr_r <= rvfm_haddr_dph;
|
||||
// Always 32-bit, always both read and write:
|
||||
rvfi_mem_rmask_r <= 4'hf;
|
||||
rvfi_mem_wmask_r <= 4'hf;
|
||||
// Has been juggled since the read that matched the winning write:
|
||||
rvfi_mem_rdata_r <= xm_result;
|
||||
// Incidentally captured on previous cycle:
|
||||
rvfi_mem_wdata_r <= rvfi_mem_wdata_r;
|
||||
end else if (bus_dph_ready_d) begin
|
||||
// RVFI has an AXI-like concept of byte strobes, rather than AHB-like
|
||||
rvfi_mem_addr_r <= rvfm_haddr_dph & 32'hffff_fffc;
|
||||
{rvfi_mem_rmask_r, rvfi_mem_wmask_r} <= 0;
|
||||
if (rvfm_htrans_dph[1] && rvfm_hwrite_dph) begin
|
||||
rvfi_mem_wmask_r <= rvfm_mem_bytemask_dph;
|
||||
rvfi_mem_wdata_r <= bus_wdata_d;
|
||||
end else if (rvfm_htrans_dph[1] && !rvfm_hwrite_dph) begin
|
||||
rvfi_mem_rmask_r <= rvfm_mem_bytemask_dph;
|
||||
rvfi_mem_rdata_r <= bus_rdata_d;
|
||||
end
|
||||
rvfi_mem_fault_r <= bus_dph_err_d;
|
||||
end else if (!rvfm_mem_hold) begin
|
||||
rvfi_mem_rmask_r <= 4'h0;
|
||||
rvfi_mem_wmask_r <= 4'h0;
|
||||
rvfi_mem_fault_r <= 1'b0;
|
||||
end
|
||||
// Also need to report rvfi_mem_fault on a fetch fault
|
||||
if (xm_except == EXCEPT_INSTR_FAULT && m_trap_enter_vld && m_trap_enter_rdy) begin
|
||||
rvfi_mem_fault_r <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Constraints
|
||||
|
||||
// Trying to keep internal constraints to a minimum.
|
||||
|
||||
// Limit sleep duration for liveness checks
|
||||
// TODO is it possible to do this in a way that doesn't assume the wakeup logic is functional?
|
||||
`ifdef RISCV_FORMAL_FAIRNESS
|
||||
reg [7:0] rvfm_sleep_counter;
|
||||
always @ (posedge clk) begin
|
||||
if (!rst_n) begin
|
||||
rvfm_sleep_counter <= 8'd00;
|
||||
end else if (xm_sleep_wfi || xm_sleep_block) begin
|
||||
rvfm_sleep_counter <= rvfm_sleep_counter + 8'h01;
|
||||
assume(rvfm_sleep_counter < 8'd5);
|
||||
end else begin
|
||||
rvfm_sleep_counter <= 8'd00;
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Tie-offs
|
||||
|
||||
// Note: Hazard3 does not have any instructions which irrversibly halt
|
||||
// execution. For the liveness check (RISCV_FORMAL_FAIRNESS is defined),
|
||||
// length of stalls is constrained and WFI is assumed to wake immediately
|
||||
// after going to sleep.
|
||||
assign rvfi_halt = 1'b0;
|
||||
|
||||
// Note: this always reports M-mode, which is not correct if the U_MODE config
|
||||
// is set. However no riscv-formal checks currently use this signal.
|
||||
assign rvfi_mode = 2'h3;
|
||||
|
||||
// Maximum XLEN is always 32 bits
|
||||
assign rvfi_ixl = 2'h1;
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2025 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Macros for building Hazard3 with RVFI trace port outside of the
|
||||
// riscv-formal test harness. For example, when including the trace port in a
|
||||
// synthesised processor.
|
||||
`ifdef HAZARD3_RVFI_STANDALONE
|
||||
|
||||
`define RISCV_FORMAL
|
||||
`define RISCV_FORMAL_NRET 1
|
||||
`define RISCV_FORMAL_XLEN 32
|
||||
`define RISCV_FORMAL_ILEN 32
|
||||
`define RISCV_FORMAL_MEM_FAULT
|
||||
`define RISCV_FORMAL_ALIGNED_MEM
|
||||
|
||||
`define RVFI_OUTPUTS \
|
||||
output wire rvfi_valid, \
|
||||
output wire [63:0] rvfi_order, \
|
||||
output wire [31:0] rvfi_insn, \
|
||||
output wire rvfi_trap, \
|
||||
output wire rvfi_halt, \
|
||||
output wire rvfi_intr, \
|
||||
output wire [1:0] rvfi_mode, \
|
||||
output wire [1:0] rvfi_ixl, \
|
||||
output wire [4:0] rvfi_rs1_addr, \
|
||||
output wire [4:0] rvfi_rs2_addr, \
|
||||
output wire [31:0] rvfi_rs1_rdata, \
|
||||
output wire [31:0] rvfi_rs2_rdata, \
|
||||
output wire [4:0] rvfi_rd_addr, \
|
||||
output wire [31:0] rvfi_rd_wdata, \
|
||||
output wire [31:0] rvfi_pc_rdata, \
|
||||
output wire [31:0] rvfi_pc_wdata, \
|
||||
output wire [31:0] rvfi_mem_addr, \
|
||||
output wire [3:0] rvfi_mem_rmask, \
|
||||
output wire [3:0] rvfi_mem_wmask, \
|
||||
output wire [31:0] rvfi_mem_rdata, \
|
||||
output wire [31:0] rvfi_mem_wdata, \
|
||||
output wire rvfi_mem_fault, \
|
||||
output wire [3:0] rvfi_mem_fault_rmask, \
|
||||
output wire [3:0] rvfi_mem_fault_wmask
|
||||
|
||||
`define RVFI_WIRES \
|
||||
wire rvfi_valid; \
|
||||
wire [63:0] rvfi_order; \
|
||||
wire [31:0] rvfi_insn; \
|
||||
wire rvfi_trap; \
|
||||
wire rvfi_halt; \
|
||||
wire rvfi_intr; \
|
||||
wire [1:0] rvfi_mode; \
|
||||
wire [1:0] rvfi_ixl; \
|
||||
wire [4:0] rvfi_rs1_addr; \
|
||||
wire [4:0] rvfi_rs2_addr; \
|
||||
wire [31:0] rvfi_rs1_rdata; \
|
||||
wire [31:0] rvfi_rs2_rdata; \
|
||||
wire [4:0] rvfi_rd_addr; \
|
||||
wire [31:0] rvfi_rd_wdata; \
|
||||
wire [31:0] rvfi_pc_rdata; \
|
||||
wire [31:0] rvfi_pc_wdata; \
|
||||
wire [31:0] rvfi_mem_addr; \
|
||||
wire [3:0] rvfi_mem_rmask; \
|
||||
wire [3:0] rvfi_mem_wmask; \
|
||||
wire [31:0] rvfi_mem_rdata; \
|
||||
wire [31:0] rvfi_mem_wdata; \
|
||||
wire rvfi_mem_fault; \
|
||||
wire [3:0] rvfi_mem_fault_rmask; \
|
||||
wire [3:0] rvfi_mem_fault_wmask;
|
||||
|
||||
`define RVFI_CONN \
|
||||
.rvfi_valid (rvfi_valid), \
|
||||
.rvfi_order (rvfi_order), \
|
||||
.rvfi_insn (rvfi_insn), \
|
||||
.rvfi_trap (rvfi_trap), \
|
||||
.rvfi_halt (rvfi_halt), \
|
||||
.rvfi_intr (rvfi_intr), \
|
||||
.rvfi_mode (rvfi_mode), \
|
||||
.rvfi_ixl (rvfi_ixl), \
|
||||
.rvfi_rs1_addr (rvfi_rs1_addr), \
|
||||
.rvfi_rs2_addr (rvfi_rs2_addr), \
|
||||
.rvfi_rs1_rdata (rvfi_rs1_rdata), \
|
||||
.rvfi_rs2_rdata (rvfi_rs2_rdata), \
|
||||
.rvfi_rd_addr (rvfi_rd_addr), \
|
||||
.rvfi_rd_wdata (rvfi_rd_wdata), \
|
||||
.rvfi_pc_rdata (rvfi_pc_rdata), \
|
||||
.rvfi_pc_wdata (rvfi_pc_wdata), \
|
||||
.rvfi_mem_addr (rvfi_mem_addr), \
|
||||
.rvfi_mem_rmask (rvfi_mem_rmask), \
|
||||
.rvfi_mem_wmask (rvfi_mem_wmask), \
|
||||
.rvfi_mem_rdata (rvfi_mem_rdata), \
|
||||
.rvfi_mem_wdata (rvfi_mem_wdata), \
|
||||
.rvfi_mem_fault (rvfi_mem_fault), \
|
||||
.rvfi_mem_fault_rmask (rvfi_mem_fault_rmask), \
|
||||
.rvfi_mem_fault_wmask (rvfi_mem_fault_wmask)
|
||||
|
||||
`endif
|
||||
+499
@@ -0,0 +1,499 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
// The Hazard3 trigger unit always implements one trigger of each of the
|
||||
// following types:
|
||||
//
|
||||
// * Instruction count trigger (type=3) with count=1 (can single-step U-mode
|
||||
// from M-mode, or step M-mode foreground from an M-mode exception handler)
|
||||
//
|
||||
// * Interrupt trigger (type=4): trigger on mask of mtip/msip/meip interrupts
|
||||
//
|
||||
// * Exception trigger (type=5): trigger on mask of exception causes
|
||||
//
|
||||
// The following are optionally supported:
|
||||
//
|
||||
// * Instruction address triggers (type=2 execute=1 select=0) aka breakpoints
|
||||
//
|
||||
// Breakpoints always use exact address matches, and the timing is always
|
||||
// "early". The number of breakpoints is configured by BREAKPOINT_TRIGGERS,
|
||||
// which can be 0.
|
||||
//
|
||||
// Interrupt/exception triggers break after the core transfers to its trap
|
||||
// handler, but before the first trap handler instruction executes. Only
|
||||
// action=1 is supported for these triggers, since trap-on-trap is useless
|
||||
// when M is the only privileged mode.
|
||||
|
||||
module hazard3_triggers #(
|
||||
`include "hazard3_config.vh"
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
|
||||
// Config interface passed through CSR block
|
||||
input wire [11:0] cfg_addr,
|
||||
input wire cfg_wen,
|
||||
input wire [W_DATA-1:0] cfg_wdata,
|
||||
output reg [W_DATA-1:0] cfg_rdata,
|
||||
|
||||
// Global trigger-to-M-mode enable from tcontrol
|
||||
input wire trig_m_en,
|
||||
|
||||
// Fetch address query from stage F
|
||||
input wire [W_ADDR-1:0] fetch_addr,
|
||||
input wire fetch_m_mode,
|
||||
input wire fetch_d_mode,
|
||||
|
||||
// Trap trigger events from stage X
|
||||
input wire event_instr_ret,
|
||||
|
||||
// Trap trigger events from stage M
|
||||
input wire event_interrupt,
|
||||
input wire event_exception,
|
||||
input wire [3:0] event_trap_cause,
|
||||
input wire event_trap_enter,
|
||||
|
||||
// F-aligned break request (for each halfword of word-sized word-aligned fetch)
|
||||
output wire [1:0] break_any,
|
||||
output wire [1:0] break_d_mode,
|
||||
|
||||
// X-aligned step break request (to M-mode only)
|
||||
output wire break_m_step,
|
||||
|
||||
// Stage-X debug mode flag, for CSR protection (may or may not be the same
|
||||
// as the query debug mode flag)
|
||||
input wire x_d_mode,
|
||||
// Stage-X M-mode flag, for enables on interrupt/exception triggers
|
||||
input wire x_m_mode
|
||||
);
|
||||
|
||||
`include "hazard3_csr_addr.vh"
|
||||
|
||||
generate
|
||||
if (DEBUG_SUPPORT == 0) begin: no_triggers
|
||||
|
||||
// The instantiation of this block should already be stubbed out in core.v if
|
||||
// there are no triggers, but we still get warnings for elaborating this
|
||||
// module with zero triggers, so add a generate block here too.
|
||||
|
||||
always @ (*) cfg_rdata = {W_DATA{1'b0}};
|
||||
assign break_any = 1'b0;
|
||||
assign break_d_mode = 1'b0;
|
||||
|
||||
end else begin: have_triggers
|
||||
|
||||
localparam TINDEX_ICOUNT = BREAKPOINT_TRIGGERS + 0;
|
||||
localparam TINDEX_INTERRUPT = BREAKPOINT_TRIGGERS + 1;
|
||||
localparam TINDEX_EXCEPTION = BREAKPOINT_TRIGGERS + 2;
|
||||
localparam N_TRIGGERS = BREAKPOINT_TRIGGERS + 3;
|
||||
|
||||
// If there are no breakpoints, we still have one dummy register (hardwired to
|
||||
// zero) for Verilog wrangling purposes. It has no synthesis effect.
|
||||
localparam N_BREAKPOINT_REGS = BREAKPOINT_TRIGGERS > 0 ? BREAKPOINT_TRIGGERS : 1;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Configuration state
|
||||
|
||||
localparam W_TSELECT = $clog2(N_TRIGGERS);
|
||||
|
||||
reg [W_TSELECT-1:0] tselect;
|
||||
|
||||
// Note tdata1 and mcontrol are the same CSR. tdata1 refers to the universal
|
||||
// fields (type/dmode) and mcontrol refers to those fields specific to
|
||||
// type=2 (address/data match), the only trigger type we implement.
|
||||
|
||||
// State for instruction address match triggers (breakpoints).
|
||||
reg bp_tdata1_dmode [0:N_BREAKPOINT_REGS-1];
|
||||
reg mcontrol_action [0:N_BREAKPOINT_REGS-1];
|
||||
reg mcontrol_m [0:N_BREAKPOINT_REGS-1];
|
||||
reg mcontrol_u [0:N_BREAKPOINT_REGS-1];
|
||||
reg mcontrol_execute [0:N_BREAKPOINT_REGS-1];
|
||||
reg [W_DATA-1:0] bp_tdata2 [0:N_BREAKPOINT_REGS-1];
|
||||
|
||||
// State for instruction count trigger
|
||||
// (hardwired: count=1 dmode=0 action=0; Debug mode single step is already
|
||||
// available via dcsr)
|
||||
reg icount_m;
|
||||
reg icount_u;
|
||||
|
||||
// State for interrupt trigger
|
||||
// (hardwired: action=1; M-mode trap-on-trap is useless as you lose the
|
||||
// original trap state)
|
||||
reg trigger_irq_m;
|
||||
reg trigger_irq_u;
|
||||
reg trigger_irq_dmode;
|
||||
reg [15:0] trigger_irq_cause;
|
||||
|
||||
localparam [15:0] IMPLEMENTED_IRQ_CAUSES = {
|
||||
4'h0, // reserved
|
||||
1'b1, // meip
|
||||
3'h0, // reserved or unimplemented
|
||||
1'b1, // mtip
|
||||
3'h0, // reserved or unimplemented
|
||||
1'b1, // msip
|
||||
3'h0 // reserved or unimplemented
|
||||
};
|
||||
|
||||
// State for exception trigger
|
||||
// (hardwired: action=1; M-mode trap-on-trap is useless as you lose the
|
||||
// original trap state)
|
||||
reg trigger_exception_m;
|
||||
reg trigger_exception_u;
|
||||
reg trigger_exception_dmode;
|
||||
reg [15:0] trigger_exception_cause;
|
||||
|
||||
localparam [15:0] IMPLEMENTED_EXCEPTION_CAUSES = {
|
||||
4'h0, // reserved
|
||||
1'b1, // 11 -> ecall from M-mode
|
||||
2'h0, // reserved or unimplemented
|
||||
|U_MODE, // 8 -> ecall from U-mode
|
||||
1'b1, // 7 -> store/AMO fault
|
||||
1'b1, // 6 -> store/AMO align
|
||||
1'b1, // 5 -> load fault
|
||||
1'b1, // 4 -> load align
|
||||
1'b0, // 3 -> breakpoint; seems useless and risky so disallow
|
||||
1'b1, // 2 -> illegal opcode
|
||||
1'b1, // 1 -> fetch fault
|
||||
~|EXTENSION_C // 0 -> fetch align (only when IALIGN is 32-bit)
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Configuration write port
|
||||
|
||||
localparam N_TRIGGERS_PADDED = 1 << $clog2(N_TRIGGERS);
|
||||
wire [N_TRIGGERS_PADDED-1:0] tselect_match = {{N_TRIGGERS_PADDED-1{1'b0}}, 1'b1} << tselect;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin: cfg_update
|
||||
integer i;
|
||||
if (!rst_n) begin
|
||||
|
||||
tselect <= {W_TSELECT{1'b0}};
|
||||
|
||||
icount_m <= 1'b0;
|
||||
icount_u <= 1'b0;
|
||||
|
||||
trigger_irq_m <= 1'b0;
|
||||
trigger_irq_u <= 1'b0;
|
||||
trigger_irq_dmode <= 1'b0;
|
||||
trigger_irq_cause <= 16'h0;
|
||||
|
||||
trigger_exception_m <= 1'b0;
|
||||
trigger_exception_u <= 1'b0;
|
||||
trigger_exception_dmode <= 1'b0;
|
||||
trigger_exception_cause <= 16'h0;
|
||||
|
||||
for (i = 0; i < BREAKPOINT_TRIGGERS; i = i + 1) begin
|
||||
bp_tdata1_dmode[i] <= 1'b0;
|
||||
mcontrol_action[i] <= 1'b0;
|
||||
mcontrol_m[i] <= 1'b0;
|
||||
mcontrol_u[i] <= 1'b0;
|
||||
mcontrol_execute[i] <= 1'b0;
|
||||
bp_tdata2[i] <= {W_DATA{1'b0}};
|
||||
end
|
||||
|
||||
end else begin
|
||||
|
||||
if (cfg_wen && cfg_addr == TSELECT) begin
|
||||
|
||||
tselect <= cfg_wdata[W_TSELECT-1:0];
|
||||
|
||||
end else if (cfg_wen && cfg_addr == TDATA1) begin
|
||||
|
||||
if (tselect_match[TINDEX_ICOUNT]) begin
|
||||
// This trigger does not implement a dmode bit, as Debug-mode
|
||||
// break on single-step is already provided by dcsr.step
|
||||
icount_m <= cfg_wdata[9];
|
||||
icount_u <= cfg_wdata[6] && |U_MODE;
|
||||
end
|
||||
if (tselect_match[TINDEX_INTERRUPT] && !(trigger_irq_dmode && !x_d_mode)) begin
|
||||
trigger_irq_dmode <= cfg_wdata[27];
|
||||
trigger_irq_m <= cfg_wdata[9];
|
||||
trigger_irq_u <= cfg_wdata[6] && |U_MODE;
|
||||
end
|
||||
if (tselect_match[TINDEX_EXCEPTION] && !(trigger_exception_dmode && !x_d_mode)) begin
|
||||
trigger_exception_dmode <= cfg_wdata[27];
|
||||
trigger_exception_m <= cfg_wdata[9];
|
||||
trigger_exception_u <= cfg_wdata[6] && |U_MODE;
|
||||
end
|
||||
for (i = 0; i < BREAKPOINT_TRIGGERS; i = i + 1) begin
|
||||
if (tselect_match[i] && !(bp_tdata1_dmode[i] && !x_d_mode)) begin
|
||||
if (x_d_mode) begin
|
||||
bp_tdata1_dmode[i] <= cfg_wdata[27];
|
||||
end
|
||||
mcontrol_action[i] <= cfg_wdata[12];
|
||||
mcontrol_m[i] <= cfg_wdata[6];
|
||||
mcontrol_u[i] <= cfg_wdata[3] && |U_MODE;
|
||||
mcontrol_execute[i] <= cfg_wdata[2];
|
||||
end
|
||||
end
|
||||
|
||||
end else if (cfg_wen && cfg_addr == TDATA2) begin
|
||||
|
||||
if (tselect_match[TINDEX_INTERRUPT] && !(trigger_irq_dmode && !x_d_mode)) begin
|
||||
trigger_irq_cause <= cfg_wdata[15:0] & IMPLEMENTED_IRQ_CAUSES;
|
||||
end
|
||||
if (tselect_match[TINDEX_EXCEPTION] && !(trigger_exception_dmode && !x_d_mode)) begin
|
||||
trigger_exception_cause <= cfg_wdata[15:0] & IMPLEMENTED_EXCEPTION_CAUSES;
|
||||
end
|
||||
for (i = 0; i < BREAKPOINT_TRIGGERS; i = i + 1) begin
|
||||
if (tselect_match[i] && !(bp_tdata1_dmode[i] && !x_d_mode)) begin
|
||||
bp_tdata2[i] <= cfg_wdata & {{W_ADDR-2{1'b1}}, |EXTENSION_C, 1'b0};
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if ((x_d_mode || event_trap_enter) && break_m_step) begin
|
||||
// count field is hardwired, so the trigger is required to disable
|
||||
// itself by clearing its own enables
|
||||
icount_m <= 1'b0;
|
||||
icount_u <= 1'b0;
|
||||
end
|
||||
|
||||
// With no breakpoints, there is still a dummy entry to avoid
|
||||
// `generate` spaghetti; tools complain about comb processes without
|
||||
// sensitivities etc, so just synchronously tie to 0:
|
||||
if (BREAKPOINT_TRIGGERS == 0) begin
|
||||
bp_tdata1_dmode[0] <= 1'b0;
|
||||
mcontrol_action[0] <= 1'b0;
|
||||
mcontrol_m[0] <= 1'b0;
|
||||
mcontrol_u[0] <= 1'b0;
|
||||
mcontrol_execute[0] <= 1'b0;
|
||||
bp_tdata2[0] <= {W_DATA{1'b0}};
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Configuration read port
|
||||
|
||||
reg [W_DATA-1:0] tdata1_rdata [0:N_TRIGGERS_PADDED-1];
|
||||
reg [W_DATA-1:0] tdata2_rdata [0:N_TRIGGERS_PADDED-1];
|
||||
reg [W_DATA-1:0] tinfo_rdata [0:N_TRIGGERS_PADDED-1];
|
||||
|
||||
always @ (*) begin: generate_padded_rdata
|
||||
|
||||
// Default for unimplemented triggers
|
||||
integer i;
|
||||
for (i = 0; i < N_TRIGGERS_PADDED; i = i + 1) begin
|
||||
tdata1_rdata[i] = {W_DATA{1'b0}};
|
||||
tdata2_rdata[i] = {W_DATA{1'b0}};
|
||||
tinfo_rdata[i] = 32'd1 << 0; // type = 0, no trigger
|
||||
end
|
||||
|
||||
// Breakpoints are the first n triggers
|
||||
for (i = 0; i < BREAKPOINT_TRIGGERS; i = i + 1) begin
|
||||
tdata1_rdata[i] = {
|
||||
4'h2, // type = address/data match
|
||||
bp_tdata1_dmode[i],
|
||||
6'h00, // maskmax = 0, exact match only
|
||||
1'b0, // hit = 0, not implemented
|
||||
1'b0, // select = 0, address match only
|
||||
1'b0, // timing = 0, trigger before execution
|
||||
2'h0, // sizelo = 0, unsized
|
||||
{3'h0, mcontrol_action[i]}, // action = 0/1, break to M-mode/D-mode
|
||||
1'b0, // chain = 0, chaining is useless for exact matches
|
||||
4'h0, // match = 0, exact match only
|
||||
mcontrol_m[i],
|
||||
1'b0,
|
||||
1'b0, // s = 0, no S-mode
|
||||
mcontrol_u[i],
|
||||
mcontrol_execute[i],
|
||||
1'b0, // store = 0, this is not a watchpoint
|
||||
1'b0 // load = 0, this is not a watchpoint
|
||||
};
|
||||
tdata2_rdata[i] = bp_tdata2[i];
|
||||
tinfo_rdata[i] = 32'd1 << 2; // type = 2, address/data match
|
||||
end
|
||||
|
||||
// Instruction count trigger
|
||||
tdata1_rdata[TINDEX_ICOUNT] = {
|
||||
4'h3, // type = instruction count
|
||||
1'b0, // dmode = 0 (Debug mode already has dcsr.step)
|
||||
2'h0, // reserved
|
||||
1'b0, // hit = 0
|
||||
14'd1, // count = 1, single-step only
|
||||
icount_m,
|
||||
1'b0, // reserved
|
||||
1'b0, // s = 0, no S-mode
|
||||
icount_u,
|
||||
6'h0 // action = 0, break to M-mode
|
||||
};
|
||||
tinfo_rdata[TINDEX_ICOUNT] = 32'd1 << 3; // type = 3, instruction count
|
||||
|
||||
// Interrupt trigger
|
||||
tdata1_rdata[TINDEX_INTERRUPT] = {
|
||||
4'h4, // type = interrupt
|
||||
trigger_irq_dmode,
|
||||
1'b0, // hit = 0
|
||||
16'h0, // reserved
|
||||
trigger_irq_m,
|
||||
1'b0, // reserved
|
||||
1'b0, // s = 0, no S-mode
|
||||
trigger_irq_u,
|
||||
6'd1 // action = 1, break to Debug mode (if dmode=1)
|
||||
};
|
||||
tdata2_rdata[TINDEX_INTERRUPT] = {
|
||||
16'h0,
|
||||
trigger_irq_cause & IMPLEMENTED_IRQ_CAUSES
|
||||
};
|
||||
tinfo_rdata[TINDEX_INTERRUPT] = 32'd1 << 4;
|
||||
|
||||
// Exception trigger
|
||||
tdata1_rdata[TINDEX_EXCEPTION] = {
|
||||
4'h5, // type = exception
|
||||
trigger_exception_dmode,
|
||||
1'b0, // hit = 0
|
||||
16'h0, // reserved
|
||||
trigger_exception_m,
|
||||
1'b0, // reserved
|
||||
1'b0, // s = 0, no S-mode
|
||||
trigger_exception_u,
|
||||
6'd1 // action = 1, break to Debug mode (if dmode=1)
|
||||
};
|
||||
tdata2_rdata[TINDEX_EXCEPTION] = {
|
||||
16'h0,
|
||||
trigger_exception_cause & IMPLEMENTED_EXCEPTION_CAUSES
|
||||
};
|
||||
tinfo_rdata[TINDEX_EXCEPTION] = 32'd1 << 5;
|
||||
|
||||
end
|
||||
|
||||
always @ (*) begin
|
||||
cfg_rdata = {W_DATA{1'b0}};
|
||||
if (cfg_addr == TSELECT) begin
|
||||
cfg_rdata = {{W_DATA-W_TSELECT{1'b0}}, tselect};
|
||||
end else if (cfg_addr == TDATA1) begin
|
||||
cfg_rdata = tdata1_rdata[tselect];
|
||||
end else if (cfg_addr == TDATA2) begin
|
||||
cfg_rdata = tdata2_rdata[tselect];
|
||||
end else if (cfg_addr == TINFO) begin
|
||||
cfg_rdata = tinfo_rdata[tselect];
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Interrupt/exception trigger logic
|
||||
|
||||
// Ignore tcontrol.mte as these triggers never target M-mode.
|
||||
wire exception_trigger_match =
|
||||
!x_d_mode && trigger_exception_dmode &&
|
||||
(x_m_mode ? trigger_exception_m : trigger_exception_u) &&
|
||||
event_exception &&
|
||||
trigger_exception_cause[event_trap_cause] &&
|
||||
IMPLEMENTED_EXCEPTION_CAUSES[event_trap_cause];
|
||||
|
||||
wire interrupt_trigger_match =
|
||||
!x_d_mode && trigger_irq_dmode &&
|
||||
(x_m_mode ? trigger_irq_m : trigger_irq_u) &&
|
||||
event_interrupt &&
|
||||
trigger_irq_cause[event_trap_cause] &&
|
||||
IMPLEMENTED_IRQ_CAUSES[event_trap_cause];
|
||||
|
||||
// Asserted no later than the end of the aphase for the instruction fetch at
|
||||
// mtvec. Tags the dphase of trap handler instruction fetches as containing
|
||||
// breakpoints.
|
||||
reg break_ie;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
break_ie <= 1'b0;
|
||||
end else begin
|
||||
break_ie <= !x_d_mode && (break_ie || (
|
||||
exception_trigger_match || interrupt_trigger_match
|
||||
));
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Instruction count trigger logic (single-step under M-mode control)
|
||||
|
||||
wire step_break_enabled = trig_m_en && !x_d_mode && (
|
||||
x_m_mode ? icount_m : icount_u
|
||||
);
|
||||
|
||||
reg break_on_step;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
break_on_step <= 1'b0;
|
||||
end else begin
|
||||
// Note icount triggers differ from dcsr.step in that they ignore
|
||||
// exceptions, only triggering on retired instructions.
|
||||
break_on_step <= !(x_d_mode || event_trap_enter) && (break_on_step || (
|
||||
event_instr_ret && step_break_enabled
|
||||
));
|
||||
end
|
||||
end
|
||||
|
||||
assign break_m_step = break_on_step;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Breakpoint trigger logic
|
||||
|
||||
// To reduce the fanin of jump and load/store gating in stage X, the address
|
||||
// lookup is in stage F (fetch data phase). We check *fetch addresses*, not
|
||||
// program counter values. Fetches are always word-sized and word-aligned.
|
||||
//
|
||||
// To ensure it is safe to do this, non-debug-mode writes to the TDATA1 and
|
||||
// TDATA2 CSRs cause a prefetch flush, to maintain write-to-fetch ordering.
|
||||
//
|
||||
// It's possible for different breakpoints to match different halfwords of the
|
||||
// fetch word. The trigger unit must report both matches separately, because
|
||||
// it is not known at this point where the instruction boundaries are (we
|
||||
// don't have the instruction data yet).
|
||||
|
||||
wire [N_BREAKPOINT_REGS-1:0] breakpoint_enabled;
|
||||
wire [N_BREAKPOINT_REGS-1:0] breakpoint_match;
|
||||
wire [N_BREAKPOINT_REGS-1:0] want_d_mode_break;
|
||||
wire [N_BREAKPOINT_REGS-1:0] want_m_mode_break;
|
||||
wire [N_BREAKPOINT_REGS-1:0] want_d_mode_break_hw0;
|
||||
wire [N_BREAKPOINT_REGS-1:0] want_d_mode_break_hw1;
|
||||
wire [N_BREAKPOINT_REGS-1:0] want_m_mode_break_hw0;
|
||||
wire [N_BREAKPOINT_REGS-1:0] want_m_mode_break_hw1;
|
||||
|
||||
genvar g;
|
||||
for (g = 0; g < N_BREAKPOINT_REGS; g = g + 1) begin: match_pc
|
||||
// Detect tripped breakpoints
|
||||
assign breakpoint_enabled[g] = mcontrol_execute[g] && !fetch_d_mode && (
|
||||
fetch_m_mode ? mcontrol_m[g] : mcontrol_u[g]
|
||||
);
|
||||
assign breakpoint_match[g] = breakpoint_enabled[g] && fetch_addr == {bp_tdata2[g][W_DATA-1:2], 2'b00};
|
||||
// Decide the type of break implied by the trip
|
||||
assign want_d_mode_break[g] = breakpoint_match[g] && mcontrol_action[g] && bp_tdata1_dmode[g];
|
||||
assign want_m_mode_break[g] = breakpoint_match[g] && !mcontrol_action[g] && trig_m_en;
|
||||
// Report separately for each halfword, so the frontend can pass this
|
||||
// through the prefetch buffer. A breakpoint exception is taken when
|
||||
// the first halfword of an instruction (of any size) is flagged with
|
||||
// a breakpoint, implying an exact match.
|
||||
assign want_d_mode_break_hw0[g] = want_d_mode_break[g] && !bp_tdata2[g][1];
|
||||
assign want_d_mode_break_hw1[g] = want_d_mode_break[g] && bp_tdata2[g][1];
|
||||
assign want_m_mode_break_hw0[g] = want_m_mode_break[g] && !bp_tdata2[g][1];
|
||||
assign want_m_mode_break_hw1[g] = want_m_mode_break[g] && bp_tdata2[g][1];
|
||||
end
|
||||
|
||||
// Break flags to frontend (tag the current fetch dphase as containing a breakpoint):
|
||||
|
||||
assign break_any = {
|
||||
|want_m_mode_break_hw1 || |want_d_mode_break_hw1 || break_ie,
|
||||
|want_m_mode_break_hw0 || |want_d_mode_break_hw0 || break_ie
|
||||
} & {2{BREAKPOINT_TRIGGERS > 0}};
|
||||
|
||||
assign break_d_mode = {
|
||||
|want_d_mode_break_hw1 || break_ie,
|
||||
|want_d_mode_break_hw0 || break_ie
|
||||
} & {2{BREAKPOINT_TRIGGERS > 0}};
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// These really ought to be localparams, but are occasionally needed for
|
||||
// passing flags around between modules, so are made available as parameters
|
||||
// instead. It's ugly, but better scope hygiene than the preprocessor. These
|
||||
// parameters should not be changed from their default values.
|
||||
|
||||
parameter W_REGADDR = 5,
|
||||
|
||||
parameter W_ALUOP = 6,
|
||||
parameter W_ALUSRC = 1,
|
||||
parameter W_MEMOP = 5,
|
||||
parameter W_BCOND = 2,
|
||||
parameter W_SHAMT = 5,
|
||||
|
||||
parameter W_EXCEPT = 4,
|
||||
parameter W_MULOP = 3
|
||||
Vendored
+276
@@ -0,0 +1,276 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
localparam RV_RS1_LSB = 15;
|
||||
localparam RV_RS1_BITS = 5;
|
||||
localparam RV_RS2_LSB = 20;
|
||||
localparam RV_RS2_BITS = 5;
|
||||
localparam RV_RD_LSB = 7;
|
||||
localparam RV_RD_BITS = 5;
|
||||
|
||||
// Note: these are preprocessor macros, rather than the usual localparams,
|
||||
// because it's quite difficult to get a definitive citation from 1364-2005
|
||||
// for whether Z values are propagated through a localparam to a casez.
|
||||
// Multiple tools complain about it, so just this once I'll use macros.
|
||||
|
||||
`ifndef HAZARD3_RVOPC_MACROS
|
||||
`define HAZARD3_RVOPC_MACROS
|
||||
|
||||
// Base ISA (some of these are Z now)
|
||||
`define RVOPC_BEQ 32'b?????????????????000?????1100011
|
||||
`define RVOPC_BNE 32'b?????????????????001?????1100011
|
||||
`define RVOPC_BLT 32'b?????????????????100?????1100011
|
||||
`define RVOPC_BGE 32'b?????????????????101?????1100011
|
||||
`define RVOPC_BLTU 32'b?????????????????110?????1100011
|
||||
`define RVOPC_BGEU 32'b?????????????????111?????1100011
|
||||
`define RVOPC_JALR 32'b?????????????????000?????1100111
|
||||
`define RVOPC_JAL 32'b?????????????????????????1101111
|
||||
`define RVOPC_LUI 32'b?????????????????????????0110111
|
||||
`define RVOPC_AUIPC 32'b?????????????????????????0010111
|
||||
`define RVOPC_ADDI 32'b?????????????????000?????0010011
|
||||
`define RVOPC_SLLI 32'b0000000??????????001?????0010011
|
||||
`define RVOPC_SLTI 32'b?????????????????010?????0010011
|
||||
`define RVOPC_SLTIU 32'b?????????????????011?????0010011
|
||||
`define RVOPC_XORI 32'b?????????????????100?????0010011
|
||||
`define RVOPC_SRLI 32'b0000000??????????101?????0010011
|
||||
`define RVOPC_SRAI 32'b0100000??????????101?????0010011
|
||||
`define RVOPC_ORI 32'b?????????????????110?????0010011
|
||||
`define RVOPC_ANDI 32'b?????????????????111?????0010011
|
||||
`define RVOPC_ADD 32'b0000000??????????000?????0110011
|
||||
`define RVOPC_SUB 32'b0100000??????????000?????0110011
|
||||
`define RVOPC_SLL 32'b0000000??????????001?????0110011
|
||||
`define RVOPC_SLT 32'b0000000??????????010?????0110011
|
||||
`define RVOPC_SLTU 32'b0000000??????????011?????0110011
|
||||
`define RVOPC_XOR 32'b0000000??????????100?????0110011
|
||||
`define RVOPC_SRL 32'b0000000??????????101?????0110011
|
||||
`define RVOPC_SRA 32'b0100000??????????101?????0110011
|
||||
`define RVOPC_OR 32'b0000000??????????110?????0110011
|
||||
`define RVOPC_AND 32'b0000000??????????111?????0110011
|
||||
`define RVOPC_LB 32'b?????????????????000?????0000011
|
||||
`define RVOPC_LH 32'b?????????????????001?????0000011
|
||||
`define RVOPC_LW 32'b?????????????????010?????0000011
|
||||
`define RVOPC_LBU 32'b?????????????????100?????0000011
|
||||
`define RVOPC_LHU 32'b?????????????????101?????0000011
|
||||
`define RVOPC_SB 32'b?????????????????000?????0100011
|
||||
`define RVOPC_SH 32'b?????????????????001?????0100011
|
||||
`define RVOPC_SW 32'b?????????????????010?????0100011
|
||||
`define RVOPC_FENCE 32'b????????????00000000000000001111
|
||||
`define RVOPC_FENCE_I 32'b00000000000000000001000000001111
|
||||
`define RVOPC_ECALL 32'b00000000000000000000000001110011
|
||||
`define RVOPC_EBREAK 32'b00000000000100000000000001110011
|
||||
`define RVOPC_CSRRW 32'b?????????????????001?????1110011
|
||||
`define RVOPC_CSRRS 32'b?????????????????010?????1110011
|
||||
`define RVOPC_CSRRC 32'b?????????????????011?????1110011
|
||||
`define RVOPC_CSRRWI 32'b?????????????????101?????1110011
|
||||
`define RVOPC_CSRRSI 32'b?????????????????110?????1110011
|
||||
`define RVOPC_CSRRCI 32'b?????????????????111?????1110011
|
||||
`define RVOPC_MRET 32'b00110000001000000000000001110011
|
||||
`define RVOPC_SYSTEM 32'b?????????????????????????1110011
|
||||
`define RVOPC_WFI 32'b00010000010100000000000001110011
|
||||
|
||||
// M extension
|
||||
`define RVOPC_MUL 32'b0000001??????????000?????0110011
|
||||
`define RVOPC_MULH 32'b0000001??????????001?????0110011
|
||||
`define RVOPC_MULHSU 32'b0000001??????????010?????0110011
|
||||
`define RVOPC_MULHU 32'b0000001??????????011?????0110011
|
||||
`define RVOPC_DIV 32'b0000001??????????100?????0110011
|
||||
`define RVOPC_DIVU 32'b0000001??????????101?????0110011
|
||||
`define RVOPC_REM 32'b0000001??????????110?????0110011
|
||||
`define RVOPC_REMU 32'b0000001??????????111?????0110011
|
||||
|
||||
// A extension
|
||||
`define RVOPC_LR_W 32'b00010??00000?????010?????0101111
|
||||
`define RVOPC_SC_W 32'b00011????????????010?????0101111
|
||||
`define RVOPC_AMOSWAP_W 32'b00001????????????010?????0101111
|
||||
`define RVOPC_AMOADD_W 32'b00000????????????010?????0101111
|
||||
`define RVOPC_AMOXOR_W 32'b00100????????????010?????0101111
|
||||
`define RVOPC_AMOAND_W 32'b01100????????????010?????0101111
|
||||
`define RVOPC_AMOOR_W 32'b01000????????????010?????0101111
|
||||
`define RVOPC_AMOMIN_W 32'b10000????????????010?????0101111
|
||||
`define RVOPC_AMOMAX_W 32'b10100????????????010?????0101111
|
||||
`define RVOPC_AMOMINU_W 32'b11000????????????010?????0101111
|
||||
`define RVOPC_AMOMAXU_W 32'b11100????????????010?????0101111
|
||||
|
||||
// Zba (address generation)
|
||||
`define RVOPC_SH1ADD 32'b0010000??????????010?????0110011
|
||||
`define RVOPC_SH2ADD 32'b0010000??????????100?????0110011
|
||||
`define RVOPC_SH3ADD 32'b0010000??????????110?????0110011
|
||||
|
||||
// Zbb (basic bit manipulation)
|
||||
`define RVOPC_ANDN 32'b0100000??????????111?????0110011
|
||||
`define RVOPC_CLZ 32'b011000000000?????001?????0010011
|
||||
`define RVOPC_CPOP 32'b011000000010?????001?????0010011
|
||||
`define RVOPC_CTZ 32'b011000000001?????001?????0010011
|
||||
`define RVOPC_MAX 32'b0000101??????????110?????0110011
|
||||
`define RVOPC_MAXU 32'b0000101??????????111?????0110011
|
||||
`define RVOPC_MIN 32'b0000101??????????100?????0110011
|
||||
`define RVOPC_MINU 32'b0000101??????????101?????0110011
|
||||
`define RVOPC_ORC_B 32'b001010000111?????101?????0010011
|
||||
`define RVOPC_ORN 32'b0100000??????????110?????0110011
|
||||
`define RVOPC_REV8 32'b011010011000?????101?????0010011
|
||||
`define RVOPC_ROL 32'b0110000??????????001?????0110011
|
||||
`define RVOPC_ROR 32'b0110000??????????101?????0110011
|
||||
`define RVOPC_RORI 32'b0110000??????????101?????0010011
|
||||
`define RVOPC_SEXT_B 32'b011000000100?????001?????0010011
|
||||
`define RVOPC_SEXT_H 32'b011000000101?????001?????0010011
|
||||
`define RVOPC_XNOR 32'b0100000??????????100?????0110011
|
||||
`define RVOPC_ZEXT_H 32'b000010000000?????100?????0110011
|
||||
|
||||
// Zbc (carry-less multiply)
|
||||
`define RVOPC_CLMUL 32'b0000101??????????001?????0110011
|
||||
`define RVOPC_CLMULH 32'b0000101??????????011?????0110011
|
||||
`define RVOPC_CLMULR 32'b0000101??????????010?????0110011
|
||||
|
||||
// Zbs (single-bit manipulation)
|
||||
`define RVOPC_BCLR 32'b0100100??????????001?????0110011
|
||||
`define RVOPC_BCLRI 32'b0100100??????????001?????0010011
|
||||
`define RVOPC_BEXT 32'b0100100??????????101?????0110011
|
||||
`define RVOPC_BEXTI 32'b0100100??????????101?????0010011
|
||||
`define RVOPC_BINV 32'b0110100??????????001?????0110011
|
||||
`define RVOPC_BINVI 32'b0110100??????????001?????0010011
|
||||
`define RVOPC_BSET 32'b0010100??????????001?????0110011
|
||||
`define RVOPC_BSETI 32'b0010100??????????001?????0010011
|
||||
|
||||
// Zbkb (basic bit manipulation for crypto) (minus those in Zbb)
|
||||
`define RVOPC_PACK 32'b0000100??????????100?????0110011
|
||||
`define RVOPC_PACKH 32'b0000100??????????111?????0110011
|
||||
`define RVOPC_BREV8 32'b011010000111?????101?????0010011
|
||||
`define RVOPC_UNZIP 32'b000010001111?????101?????0010011
|
||||
`define RVOPC_ZIP 32'b000010001111?????001?????0010011
|
||||
|
||||
// Zbkc is a subset of Zbc.
|
||||
|
||||
// Zbkx (crossbar permutation)
|
||||
`define RVOPC_XPERM8 32'b0010100??????????100?????0110011
|
||||
`define RVOPC_XPERM4 32'b0010100??????????010?????0110011
|
||||
|
||||
// Zilsd (load/store pair)
|
||||
`define RVOPC_LD 32'b?????????????????011????00000011 // rd[0] == 0
|
||||
`define RVOPC_SD 32'b???????????0?????011?????0100011 // rs2[0] == 0
|
||||
|
||||
// Hazard3 custom instructions
|
||||
|
||||
// Xh3bextm (Hazard3 multi-bit extract): multi-bit versions of bext/bexti from Zbs
|
||||
`define RVOPC_H3_BEXTM 32'b000???0??????????000?????0001011 // custom-0 funct3=0
|
||||
`define RVOPC_H3_BEXTMI 32'b000???0??????????100?????0001011 // custom-0 funct3=4
|
||||
|
||||
// C Extension
|
||||
`define RVOPC_C_ADDI4SPN 16'b000???????????00 // *** illegal if imm 0
|
||||
`define RVOPC_C_LW 16'b010???????????00
|
||||
`define RVOPC_C_SW 16'b110???????????00
|
||||
|
||||
`define RVOPC_C_ADDI 16'b000???????????01
|
||||
`define RVOPC_C_JAL 16'b001???????????01
|
||||
`define RVOPC_C_J 16'b101???????????01
|
||||
`define RVOPC_C_LI 16'b010???????????01
|
||||
// addi16sp when rd=2:
|
||||
`define RVOPC_C_LUI 16'b011???????????01 // *** reserved if imm 0 (for both LUI and ADDI16SP)
|
||||
`define RVOPC_C_SRLI 16'b100000????????01 // On RV32 imm[5] (instr[12]) must be 0, else reserved NSE.
|
||||
`define RVOPC_C_SRAI 16'b100001????????01 // On RV32 imm[5] (instr[12]) must be 0, else reserved NSE.
|
||||
`define RVOPC_C_ANDI 16'b100?10????????01
|
||||
`define RVOPC_C_SUB 16'b100011???00???01
|
||||
`define RVOPC_C_XOR 16'b100011???01???01
|
||||
`define RVOPC_C_OR 16'b100011???10???01
|
||||
`define RVOPC_C_AND 16'b100011???11???01
|
||||
`define RVOPC_C_BEQZ 16'b110???????????01
|
||||
`define RVOPC_C_BNEZ 16'b111???????????01
|
||||
|
||||
`define RVOPC_C_SLLI 16'b0000??????????10 // On RV32 imm[5] (instr[12]) must be 0, else reserved NSE.
|
||||
// jr if !rs2:
|
||||
`define RVOPC_C_MV 16'b1000??????????10 // *** reserved if JR and !rs1 (instr[11:7])
|
||||
// jalr if !rs2:
|
||||
`define RVOPC_C_ADD 16'b1001??????????10 // *** EBREAK if !instr[11:2]
|
||||
`define RVOPC_C_LWSP 16'b010???????????10 // *** reserved if rd=x0
|
||||
`define RVOPC_C_SWSP 16'b110???????????10
|
||||
|
||||
// Zcb simple additional compressed instructions
|
||||
`define RVOPC_C_LBU 16'b100000????????00
|
||||
`define RVOPC_C_LHU 16'b100001???0????00
|
||||
`define RVOPC_C_LH 16'b100001???1????00
|
||||
`define RVOPC_C_SB 16'b100010????????00
|
||||
`define RVOPC_C_SH 16'b100011???0????00
|
||||
`define RVOPC_C_ZEXT_B 16'b100111???1100001
|
||||
`define RVOPC_C_SEXT_B 16'b100111???1100101
|
||||
`define RVOPC_C_ZEXT_H 16'b100111???1101001
|
||||
`define RVOPC_C_SEXT_H 16'b100111???1101101
|
||||
`define RVOPC_C_NOT 16'b100111???1110101
|
||||
`define RVOPC_C_MUL 16'b100111???10???01
|
||||
|
||||
// Zclsd load/store pair instructions
|
||||
`define RVOPC_C_LD 16'b011??????????000 // rd[0] == 0
|
||||
`define RVOPC_C_LDSP 16'b011?????0?????10 // rd[0] == 0
|
||||
`define RVOPC_C_SD 16'b111??????????000 // rs2[0] == 0
|
||||
`define RVOPC_C_SDSP 16'b111??????????010 // rs2[0] == 0
|
||||
|
||||
// Zcmp push/pop instructions
|
||||
`define RVOPC_CM_PUSH 16'b10111000??????10
|
||||
`define RVOPC_CM_POP 16'b10111010??????10
|
||||
`define RVOPC_CM_POPRETZ 16'b10111100??????10
|
||||
`define RVOPC_CM_POPRET 16'b10111110??????10
|
||||
`define RVOPC_CM_MVSA01 16'b101011???01???10
|
||||
`define RVOPC_CM_MVA01S 16'b101011???11???10
|
||||
|
||||
// Copies provided here with 0 instead of ? so that these can be used to build 32-bit instructions in the decompressor
|
||||
|
||||
`define RVOPC_NOZ_BEQ 32'b00000000000000000000000001100011
|
||||
`define RVOPC_NOZ_BNE 32'b00000000000000000001000001100011
|
||||
`define RVOPC_NOZ_BLT 32'b00000000000000000100000001100011
|
||||
`define RVOPC_NOZ_BGE 32'b00000000000000000101000001100011
|
||||
`define RVOPC_NOZ_BLTU 32'b00000000000000000110000001100011
|
||||
`define RVOPC_NOZ_BGEU 32'b00000000000000000111000001100011
|
||||
`define RVOPC_NOZ_JALR 32'b00000000000000000000000001100111
|
||||
`define RVOPC_NOZ_JAL 32'b00000000000000000000000001101111
|
||||
`define RVOPC_NOZ_LUI 32'b00000000000000000000000000110111
|
||||
`define RVOPC_NOZ_AUIPC 32'b00000000000000000000000000010111
|
||||
`define RVOPC_NOZ_ADDI 32'b00000000000000000000000000010011
|
||||
`define RVOPC_NOZ_SLLI 32'b00000000000000000001000000010011
|
||||
`define RVOPC_NOZ_SLTI 32'b00000000000000000010000000010011
|
||||
`define RVOPC_NOZ_SLTIU 32'b00000000000000000011000000010011
|
||||
`define RVOPC_NOZ_XORI 32'b00000000000000000100000000010011
|
||||
`define RVOPC_NOZ_SRLI 32'b00000000000000000101000000010011
|
||||
`define RVOPC_NOZ_SRAI 32'b01000000000000000101000000010011
|
||||
`define RVOPC_NOZ_ORI 32'b00000000000000000110000000010011
|
||||
`define RVOPC_NOZ_ANDI 32'b00000000000000000111000000010011
|
||||
`define RVOPC_NOZ_ADD 32'b00000000000000000000000000110011
|
||||
`define RVOPC_NOZ_SUB 32'b01000000000000000000000000110011
|
||||
`define RVOPC_NOZ_SLL 32'b00000000000000000001000000110011
|
||||
`define RVOPC_NOZ_SLT 32'b00000000000000000010000000110011
|
||||
`define RVOPC_NOZ_SLTU 32'b00000000000000000011000000110011
|
||||
`define RVOPC_NOZ_XOR 32'b00000000000000000100000000110011
|
||||
`define RVOPC_NOZ_SRL 32'b00000000000000000101000000110011
|
||||
`define RVOPC_NOZ_SRA 32'b01000000000000000101000000110011
|
||||
`define RVOPC_NOZ_OR 32'b00000000000000000110000000110011
|
||||
`define RVOPC_NOZ_AND 32'b00000000000000000111000000110011
|
||||
`define RVOPC_NOZ_LB 32'b00000000000000000000000000000011
|
||||
`define RVOPC_NOZ_LH 32'b00000000000000000001000000000011
|
||||
`define RVOPC_NOZ_LW 32'b00000000000000000010000000000011
|
||||
`define RVOPC_NOZ_LBU 32'b00000000000000000100000000000011
|
||||
`define RVOPC_NOZ_LHU 32'b00000000000000000101000000000011
|
||||
`define RVOPC_NOZ_SB 32'b00000000000000000000000000100011
|
||||
`define RVOPC_NOZ_SH 32'b00000000000000000001000000100011
|
||||
`define RVOPC_NOZ_SW 32'b00000000000000000010000000100011
|
||||
`define RVOPC_NOZ_FENCE 32'b00000000000000000000000000001111
|
||||
`define RVOPC_NOZ_FENCE_I 32'b00000000000000000001000000001111
|
||||
`define RVOPC_NOZ_ECALL 32'b00000000000000000000000001110011
|
||||
`define RVOPC_NOZ_EBREAK 32'b00000000000100000000000001110011
|
||||
`define RVOPC_NOZ_CSRRW 32'b00000000000000000001000001110011
|
||||
`define RVOPC_NOZ_CSRRS 32'b00000000000000000010000001110011
|
||||
`define RVOPC_NOZ_CSRRC 32'b00000000000000000011000001110011
|
||||
`define RVOPC_NOZ_CSRRWI 32'b00000000000000000101000001110011
|
||||
`define RVOPC_NOZ_CSRRSI 32'b00000000000000000110000001110011
|
||||
`define RVOPC_NOZ_CSRRCI 32'b00000000000000000111000001110011
|
||||
`define RVOPC_NOZ_SYSTEM 32'b00000000000000000000000001110011
|
||||
|
||||
// Non-RV32I instructions for Zcb:
|
||||
`define RVOPC_NOZ_MUL 32'b00000010000000000000000000110011
|
||||
`define RVOPC_NOZ_SEXT_B 32'b01100000010000000001000000010011
|
||||
`define RVOPC_NOZ_SEXT_H 32'b01100000010100000001000000010011
|
||||
`define RVOPC_NOZ_ZEXT_H 32'b00001000000000000100000000110011
|
||||
|
||||
// Non-RV32I instructions for Zclsd:
|
||||
`define RVOPC_NOZ_LD 32'b00000000000000000011000000000011
|
||||
`define RVOPC_NOZ_SD 32'b00000000000000000011000000100011
|
||||
|
||||
`endif
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
# Set up root paths used by Makefiles (there is a lot of cross-referencing,
|
||||
# e.g. tests referencing the HDL directory). This .mk file is
|
||||
# (eventually) included by every Makefile in the project.
|
||||
|
||||
PROJ_ROOT := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
HDL := $(PROJ_ROOT)/hdl
|
||||
SCRIPTS := $(PROJ_ROOT)/scripts
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
FPGA Scripts
|
||||
============
|
||||
|
||||
A loose collection of scripts I've collected while playing with FPGAs at home. Simulation, synthesis, so forth. All of them are terrible, some of them work.
|
||||
|
||||
The most useful one is `listfiles` which provides a simple way of describing source-level dependencies and producing file lists for e.g. a synthesis tool.
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Based on Commander Keen compresssion algorithm (a variant of LZSS)
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
def get_symbol(data, window):
|
||||
it = iter(data)
|
||||
needle = bytearray([next(it)])
|
||||
try:
|
||||
while window.find(needle) >= 0:
|
||||
needle.append(next(it))
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
matchlen = len(needle) - 1
|
||||
if matchlen <= 1:
|
||||
return (1, (False, needle[0]))
|
||||
else:
|
||||
matchpos = len(window) - 1 - window.find(needle[:-1])
|
||||
return (matchlen, (True, matchpos, matchlen - 1))
|
||||
|
||||
def iter_symbols(data, windowsize):
|
||||
data = data[:]
|
||||
window = bytearray()
|
||||
|
||||
while True:
|
||||
consumed, symbol = get_symbol(data, window)
|
||||
yield symbol
|
||||
del window[:max(0, len(window) + consumed - windowsize)]
|
||||
window.extend(data[:consumed])
|
||||
del data[:consumed]
|
||||
|
||||
def bitmap(bits):
|
||||
bm = 0
|
||||
for i, bit in enumerate(bits):
|
||||
bm = bm | (bool(bit) << i)
|
||||
return bm
|
||||
|
||||
def iter_bytes(data, windowsize):
|
||||
syms = iter(iter_symbols(data, windowsize))
|
||||
holding = []
|
||||
eof = False
|
||||
while not eof:
|
||||
try:
|
||||
holding.append(next(syms))
|
||||
except StopIteration:
|
||||
eof = True
|
||||
if eof and len(holding) or len(holding) >= 8:
|
||||
yield bitmap(sym[0] for sym in holding)
|
||||
for sym in holding:
|
||||
yield from sym[1:]
|
||||
holding.clear()
|
||||
|
||||
def compress(data, windowsize):
|
||||
return bytearray(iter_bytes(data, windowsize))
|
||||
|
||||
def decompress(cdata, windowsize):
|
||||
out = bytearray()
|
||||
window = bytearray()
|
||||
symbolcount = 0
|
||||
it = iter(cdata)
|
||||
while True:
|
||||
if symbolcount == 0:
|
||||
try:
|
||||
bitmap = next(it)
|
||||
except StopIteration:
|
||||
break
|
||||
symbolcount = 7
|
||||
else:
|
||||
symbolcount -= 1
|
||||
if bitmap & 1:
|
||||
start = len(window) - 1 - next(it)
|
||||
count = next(it) + 1
|
||||
new = window[start:start + count]
|
||||
out.extend(new)
|
||||
window.extend(new)
|
||||
else:
|
||||
try:
|
||||
lit = next(it)
|
||||
except StopIteration:
|
||||
break
|
||||
window.append(lit)
|
||||
out.append(lit)
|
||||
del window[:max(0, len(window) - windowsize)]
|
||||
bitmap = bitmap >> 1
|
||||
|
||||
return out
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument("input", help="Input file name")
|
||||
parser.add_argument("output", help="Output file name")
|
||||
parser.add_argument("-d", "--decompress", action="store_true", help="Decompress input (default is to compress)")
|
||||
parser.add_argument("-w", "--window", help="Compression window size")
|
||||
args = parser.parse_args()
|
||||
if args.input == "-":
|
||||
ifile = sys.stdin
|
||||
else:
|
||||
ifile = open(args.input, "rb")
|
||||
if args.output == "-":
|
||||
ofile = sys.stdout
|
||||
else:
|
||||
ofile = open(args.output, "wb")
|
||||
if args.window is None:
|
||||
args.window = 256
|
||||
|
||||
ibuf = bytearray(ifile.read())
|
||||
ifile.close()
|
||||
|
||||
if args.decompress:
|
||||
obuf = decompress(ibuf, args.window)
|
||||
else:
|
||||
obuf = compress(ibuf, args.window)
|
||||
|
||||
ofile.write(obuf)
|
||||
ofile.close()
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
# Turn KiCad netlist into a PCF file for icestorm
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import shlex
|
||||
from collections import OrderedDict
|
||||
|
||||
def sanitize_net_name(s):
|
||||
s = s.replace("~", "")
|
||||
s = re.sub(r"(\d+)$", r"[\1]", s)
|
||||
return s
|
||||
|
||||
def natural_key(s):
|
||||
return tuple(int(x) if x.isdigit() else x for x in re.split(r"(\d+)", s))
|
||||
|
||||
def extract_nets(fh, ref, filters):
|
||||
nets = []
|
||||
|
||||
name = None
|
||||
namevalid = False
|
||||
|
||||
for l in fh:
|
||||
m = re.match(r"^\s*\(net \([^\)]*\) \(name ([^\)]+)\)", l)
|
||||
if m:
|
||||
name = m.group(1).strip().strip("/").lower()
|
||||
namevalid = not any(name.startswith(s) for s in [
|
||||
"\"net", "+", "-", "gnd", "vcc", "vdd", "vss", *filters
|
||||
])
|
||||
elif namevalid:
|
||||
m = re.match(r"^\s*\(node \(ref ([^\)]+)\) \(pin ([^\)]+)\)", l)
|
||||
if m and m.group(1) == ref:
|
||||
nets.append((name, m.group(2)))
|
||||
return nets
|
||||
|
||||
def align_buses(nets):
|
||||
buses = OrderedDict()
|
||||
onets = []
|
||||
# First figure out what is/isn't a bus
|
||||
for net in nets:
|
||||
if "[" in net[0]:
|
||||
busname = net[0].split("[")[0]
|
||||
if busname not in buses:
|
||||
buses[busname] = []
|
||||
buses[busname].append(net)
|
||||
else:
|
||||
onets.append(net)
|
||||
# Then right-justify the buses
|
||||
for busname, bus in buses.items():
|
||||
indices = list(int(net[0].split('[')[1].strip(']')) for net in bus)
|
||||
lsb = min(indices)
|
||||
for index, net in zip(indices, bus):
|
||||
onets.append(("{}[{}]".format(busname, index - lsb), net[1]))
|
||||
return onets
|
||||
|
||||
|
||||
def write_pcf(fh, nets):
|
||||
fh.write("# Generated with extract_ref_nets\n\n")
|
||||
for name, loc in sorted(nets, key = lambda x: natural_key(x[0])):
|
||||
fh.write("set_io {:<20} {}\n".format(name, loc))
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument("netfile", help="Path to KiCad .net file")
|
||||
parser.add_argument("ref", help="Component reference whose named nets will be extracted")
|
||||
parser.add_argument("--output", "-o", help="Output file name")
|
||||
parser.add_argument("--filters", "-f", help="-f \"abc xyz\": filter out nets beginning with abc or xyz")
|
||||
args = parser.parse_args()
|
||||
opath = "chip.pcf" if args.output is None else args.output
|
||||
filters = [] if args.filters is None else shlex.split(args.filters)
|
||||
with open(args.netfile) as ifile:
|
||||
nets = extract_nets(ifile, args.ref, filters)
|
||||
nets = map(lambda n: (sanitize_net_name(n[0]), n[1]), nets)
|
||||
nets = align_buses(nets)
|
||||
with open(opath, "w") as ofile:
|
||||
write_pcf(ofile, nets)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
# Must define:
|
||||
# DOTF: .f file containing root of file list
|
||||
# TOP: name of top-level module
|
||||
|
||||
SRCS=$(shell PROJ_ROOT=$(PROJ_ROOT) HDL=$(HDL) $(SCRIPTS)/listfiles --relative -f flat $(DOTF))
|
||||
INCDIRS=$(shell PROJ_ROOT=$(PROJ_ROOT) HDL=$(HDL) $(SCRIPTS)/listfiles --relative -f flati $(DOTF))
|
||||
|
||||
YOSYS=yosys
|
||||
YOSYS_SMTBMC=$(YOSYS)-smtbmc
|
||||
|
||||
DEPTH?=20
|
||||
COVER_APPEND?=3
|
||||
YOSYS_SMT_SOLVER?=z3
|
||||
|
||||
DEFINES?=
|
||||
|
||||
PREP_CMD =read_verilog -formal
|
||||
PREP_CMD+=$(addprefix -I,$(INCDIRS))
|
||||
PREP_CMD+=$(addprefix -D,$(DEFINES) )
|
||||
PREP_CMD+= $(SRCS);
|
||||
PREP_CMD+=prep -top $(TOP); async2sync; dffunmap; write_smt2 -wires $(TOP).smt2
|
||||
|
||||
BMC_ARGS=-s $(YOSYS_SMT_SOLVER) --dump-vcd $(TOP).vcd -t $(DEPTH)
|
||||
IND_ARGS=-i $(BMC_ARGS)
|
||||
COV_ARGS = -c $(BMC_ARGS) --append $(COVER_APPEND)
|
||||
.PHONY: prove prep bmc induct clean
|
||||
|
||||
prove: bmc induct
|
||||
|
||||
prep:
|
||||
$(YOSYS) -p "$(PREP_CMD)" > prep.log
|
||||
|
||||
bmc: prep
|
||||
$(YOSYS_SMTBMC) $(BMC_ARGS) $(TOP).smt2 | tee bmc.log
|
||||
|
||||
induct: prep
|
||||
$(YOSYS_SMTBMC) $(IND_ARGS) $(TOP).smt2 | tee induct.log
|
||||
|
||||
cover: prep
|
||||
$(YOSYS_SMTBMC) $(COV_ARGS) $(TOP).smt2 | tee cover.log
|
||||
|
||||
clean::
|
||||
rm -f $(TOP).vcd $(TOP).smt2 srcs.mk prep.log bmc.log induct.log cover.log
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
# Navigate up directories until we find a file called "Default.wcfg"
|
||||
# Stop if we get to root
|
||||
|
||||
set cfgdir [file normalize .];
|
||||
while {"$cfgdir" != "/"} {
|
||||
if [file exists $cfgdir/Default.wcfg] {
|
||||
wcfg open $cfgdir/Default.wcfg
|
||||
break
|
||||
}
|
||||
set cfgdir [file normalize $cfgdir/..]
|
||||
}
|
||||
|
||||
run 100us;
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import shlex
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import re
|
||||
|
||||
help_str = """
|
||||
|
||||
Tool for building file lists, into formats required by various tools.
|
||||
".f" files contain four types of command:
|
||||
|
||||
file (filename)
|
||||
adds a file to the list
|
||||
include (dir)
|
||||
add a directory to include path, if output format supports this
|
||||
wildcard (.extension) (dir)
|
||||
add all files with a given extension in a given directory
|
||||
list (filename)
|
||||
recurse on another filelist file
|
||||
|
||||
The idea is that each component in a project has a
|
||||
.f file which lists all of the Verilog (e.g.) files
|
||||
for that component. Higher-level .f files will hierarchically
|
||||
include the lower-level ones.
|
||||
|
||||
In this way, you can build large flat file lists to pass into the
|
||||
various tools, but never have to *write* large flat file lists.
|
||||
|
||||
It also makes it easier to specify parts of your design hierarchy
|
||||
for a given tool. For example, if you just want to synthesise your
|
||||
CPU, you can run "listfiles cpu.f -f flat"
|
||||
"""
|
||||
|
||||
def wildcard(dir, extension):
|
||||
prev_dir = os.getcwd()
|
||||
os.chdir(dir)
|
||||
files = [os.path.abspath(f) for f in os.listdir() if os.path.splitext(f)[-1] == extension]
|
||||
os.chdir(prev_dir)
|
||||
return files
|
||||
|
||||
def read_filelist(fname):
|
||||
files = []
|
||||
includes = []
|
||||
f = open(fname)
|
||||
prev_dir = os.getcwd()
|
||||
os.chdir(os.path.dirname(os.path.abspath(fname)))
|
||||
for l in f.readlines():
|
||||
l = l.split("#")[0].strip()
|
||||
if l == "":
|
||||
continue
|
||||
words = shlex.split(l)
|
||||
if words[0] == "file":
|
||||
assert(len(words) == 2)
|
||||
files.append(os.path.abspath(os.path.expandvars(words[1])))
|
||||
elif words[0] == "include":
|
||||
assert(len(words) == 2)
|
||||
includes.append(os.path.abspath(os.path.expandvars(words[1])))
|
||||
elif words[0] == "wildcard":
|
||||
assert(len(words) == 3)
|
||||
files.extend(wildcard(os.path.expandvars(words[2]), words[1]))
|
||||
elif words[0] == "list":
|
||||
assert(len(words) == 2)
|
||||
newfiles, newincludes = read_filelist(os.path.expandvars(words[1]))
|
||||
files.extend(newfiles)
|
||||
includes.extend(newincludes)
|
||||
else:
|
||||
raise Exception("In filelist {}: Invalid command \"{}\"".format(fname, words[0]))
|
||||
os.chdir(prev_dir)
|
||||
return (files, includes)
|
||||
|
||||
formats = {
|
||||
"isim": lambda f, i: "verilog work {} {}\n".format(" ".join(f), " ".join("-i " + inc for inc in i)),
|
||||
"flat": lambda f, i: " ".join(f) + "\n",
|
||||
"flati": lambda f, i: " ".join(i) + "\n",
|
||||
"make": lambda f, i: "SRCS={}\nINCDIRS={}\n".format(" ".join(f), " ".join(i))
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.epilog = help_str
|
||||
parser.add_argument("src", help="File list source file")
|
||||
parser.add_argument("--format", "-f", help="Format to generate output in. Allowed: isim, make, flat (default)")
|
||||
parser.add_argument("--relative", "-r", action="store_true", help="Use relative paths in output file")
|
||||
parser.add_argument("--relativeto", help="Use relative paths, relative to some specified path")
|
||||
parser.add_argument("--output", "-o", help="Output file name")
|
||||
args = parser.parse_args()
|
||||
if args.format is None:
|
||||
args.format = "flat"
|
||||
files, includes = read_filelist(args.src)
|
||||
# Uniquify whilst preserving order
|
||||
func = lambda l: list(dict.fromkeys(l))
|
||||
if args.relative:
|
||||
func = lambda l, func=func: [os.path.relpath(f) for f in func(l)]
|
||||
elif args.relativeto:
|
||||
func = lambda l, func=func: [os.path.relpath(f, args.relativeto) for f in func(l)]
|
||||
|
||||
files, includes = map(func, (files, includes))
|
||||
if args.format not in formats:
|
||||
sys.exit("Unknown format: " + args.format)
|
||||
ofile = sys.stdout if args.output is None else open(args.output, "w")
|
||||
ofile.write(formats[args.format](files, includes))
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import struct
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("ifile")
|
||||
parser.add_argument("ofile")
|
||||
args = parser.parse_args()
|
||||
data = open(args.ifile, "rb").read()
|
||||
with open(args.ofile, "wb") as ofile:
|
||||
ofile.write("RISCBoy".encode() + bytes(1))
|
||||
ofile.write(struct.pack("<L", len(data)))
|
||||
ofile.write(data)
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Prepend a length and CRC32 checksum to a binary file so that it can be sent
|
||||
# to the DOOMSoC UART bootloader. Both values are unsigned 32-bit
|
||||
# little-endian. CRC32 is calculated with standard parameters (input and
|
||||
# output reflected, seed all-ones, final XOR all-ones).
|
||||
|
||||
import argparse
|
||||
import binascii
|
||||
import struct
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("ifile")
|
||||
parser.add_argument("ofile")
|
||||
args = parser.parse_args()
|
||||
|
||||
data = open(args.ifile, "rb").read()
|
||||
|
||||
with open(args.ofile, "wb") as ofile:
|
||||
ofile.write(struct.pack("<L", len(data)))
|
||||
ofile.write(struct.pack("<L", binascii.crc32(data)))
|
||||
ofile.write(data)
|
||||
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from PIL import Image
|
||||
import argparse
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
|
||||
FORMATS = ["argb1555", "rgab5515", "bgar5515", "rgb565", "argb1232", "ragb2132", "rgb332", "r2", "r1", "p8", "p4", "p2", "p1"]
|
||||
|
||||
def bytes_from_bitstream_le(bitstream):
|
||||
accum = 0
|
||||
accum_size = 0
|
||||
while True:
|
||||
while accum_size < 8:
|
||||
try:
|
||||
nbits, newdata = next(bitstream)
|
||||
except StopIteration:
|
||||
return
|
||||
accum = accum | (newdata << accum_size)
|
||||
accum_size += nbits
|
||||
while accum_size >= 8:
|
||||
yield accum & 0xff
|
||||
accum = accum >> 8
|
||||
accum_size -= 8
|
||||
|
||||
class BinHeader:
|
||||
def __init__(self, filename, arrayname=None):
|
||||
if arrayname is None:
|
||||
arrayname = filename.split(".")[0]
|
||||
self.f = open(filename, "w")
|
||||
self.out_count = 0
|
||||
self.f.write(
|
||||
"#ifndef _IMG_ASSET_SECTION\n" \
|
||||
"#define _IMG_ASSET_SECTION \".data\"\n" \
|
||||
"#endif\n\n" \
|
||||
f"static const char __attribute__((aligned(16), section(_IMG_ASSET_SECTION \".{arrayname}\"))) {arrayname}[] = {{\n\t"
|
||||
)
|
||||
|
||||
def write(self, bs):
|
||||
for b in bs:
|
||||
self.f.write("0x{:02x}".format(b) + (",\n\t" if self.out_count % 16 == 15 else ", "))
|
||||
self.out_count += 1
|
||||
|
||||
def close(self):
|
||||
self.f.write("\n};\n")
|
||||
self.f.close()
|
||||
|
||||
# Fixed dither -- note every number 0...15 appears once (thanks Graham)
|
||||
dither_pattern_4x4 = [
|
||||
[0 , 8 , 2 , 10],
|
||||
[12 , 4 , 14 , 6 ],
|
||||
[3 , 11 , 1 , 9 ],
|
||||
[15 , 7 , 13 , 5 ],
|
||||
]
|
||||
|
||||
def format_channel(data, msb, lsb, dither=False, dithercoord=None):
|
||||
# Assume data to be 8 bits
|
||||
out_width = msb - lsb + 1
|
||||
assert(out_width <= 8)
|
||||
if dither:
|
||||
ditherval = dither_pattern_4x4[dithercoord[1] % 4][dithercoord[0] % 4]
|
||||
shamt = (8 - out_width) - 4
|
||||
if shamt >= 0:
|
||||
data += ditherval << shamt
|
||||
else:
|
||||
data += ditherval >> -shamt
|
||||
data = min(data, 0xff)
|
||||
return (data >> (8 - out_width)) << lsb
|
||||
|
||||
def format_rgb_pixel(pix, fmt, dither=False, dithercoord=None):
|
||||
accum = 0
|
||||
for p, f in zip(pix, fmt):
|
||||
accum |= format_channel(p, f[0], f[1], dither, dithercoord)
|
||||
if len(pix) == len(fmt) - 1:
|
||||
accum |= format_channel(0xff, fmt[-1][0], fmt[-1][1])
|
||||
return accum
|
||||
|
||||
# TODO would be kind of nice to generate these based on format string but I don't need that yet
|
||||
rgb_formats = {
|
||||
"argb1555": (16, ((14, 10), (9, 5), (4, 0), (15, 15))),
|
||||
"rgab5515": (16, ((15, 11), (10, 6), (4, 0), (5, 5))),
|
||||
"bgar5515": (16, ((4, 0), (10, 6), (15, 11), (5, 5))),
|
||||
"rgb565" : (16, ((15, 11), (10, 5), (4, 0))),
|
||||
"argb1232": (8, ((6, 5), (4, 2), (1, 0), (7, 7))),
|
||||
"ragb2132": (8, ((7, 6), (4, 2), (1, 0), (5, 5))),
|
||||
"rgb332" : (8, ((7, 5), (4, 2), (1, 0))),
|
||||
"r2" : (2, ((1, 0), (-1, 0), (-1, 0))),
|
||||
"r1" : (1, ((0, 0), (-1, 0), (-1, 0))),
|
||||
}
|
||||
|
||||
def format_pixel(format, src_has_transparency, pixel, dither=False, dithercoord=None):
|
||||
assert(format in FORMATS)
|
||||
if format in rgb_formats:
|
||||
return (rgb_formats[format][0], format_rgb_pixel(pixel, rgb_formats[format][1], dither, dithercoord))
|
||||
elif format in ["p8", "p4", "p2", "p1"]:
|
||||
size = int(format[1:])
|
||||
return (size, (pixel + src_has_transparency) & ((1 << size) - 1))
|
||||
else:
|
||||
raise Exception()
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("input", help="Input file name")
|
||||
parser.add_argument("output", help="Output file name")
|
||||
parser.add_argument("--tilesize", "-t", help="Tile size (pixels), default 8",
|
||||
default="8", choices=[str(2 ** i) for i in range(3, 11)])
|
||||
parser.add_argument("--single", "-s", action="store_true",
|
||||
help="The input consists of a single image of arbitrary width/height, rather than a tileset")
|
||||
parser.add_argument("--format", "-f", help="Output pixel format, default argb1555",
|
||||
default="argb1555", choices=FORMATS)
|
||||
parser.add_argument("--dither", "-d", action="store_true",
|
||||
help="Apply a simple fixed dither pattern when packing RGB files")
|
||||
parser.add_argument("--metadata", "-m", action="store_true",
|
||||
help="Write out opacity metadata at end of file for faster alpha blit (must be used with --single)")
|
||||
args = parser.parse_args()
|
||||
img = Image.open(args.input)
|
||||
if args.single:
|
||||
tsize_x = img.width
|
||||
tsize_y = img.height
|
||||
else:
|
||||
tsize_x = int(args.tilesize)
|
||||
tsize_y = tsize_x
|
||||
if args.metadata and not args.single:
|
||||
sys.exit("--metadata must be used with --single")
|
||||
|
||||
format_is_paletted = args.format.startswith("p")
|
||||
image_is_transparent = img.mode == "RGBA" and img.getextrema()[3][0] < 255
|
||||
if args.metadata and not image_is_transparent:
|
||||
sys.exit("Can't write opacity metadata for a non-transparent image")
|
||||
|
||||
friendly_out_name = os.path.basename(args.input).split(".")[0]
|
||||
|
||||
if format_is_paletted:
|
||||
ncolours_max = 1 << int(args.format[1:])
|
||||
ncolours_actual = min(ncolours_max, len(img.getcolors()))
|
||||
pimg = img.quantize(ncolours_max)
|
||||
palette = pimg.getpalette()
|
||||
# TODO haven't found a sane way to make PIL map transparency to palette
|
||||
if image_is_transparent:
|
||||
for x in range(img.width):
|
||||
for y in range(img.height):
|
||||
if not (img.getpixel((x, y))[3] & 0x80):
|
||||
pimg.putpixel((x, y), 255)
|
||||
|
||||
if args.output.endswith(".h"):
|
||||
pfile = BinHeader(args.output + ".pal", arrayname=friendly_out_name + "_pal")
|
||||
else:
|
||||
pfile = open(args.output + ".pal", "wb")
|
||||
if image_is_transparent:
|
||||
pfile.write(bytes(2))
|
||||
pfile.write(bytes(bytes_from_bitstream_le(
|
||||
format_pixel("argb1555", False, palette[i:i+3]) for i in range(0, ncolours_actual * 3, 3)
|
||||
)))
|
||||
if ncolours_actual < ncolours_max:
|
||||
pfile.write(bytes(2 * (ncolours_max - ncolours_actual)))
|
||||
pfile.close()
|
||||
|
||||
img = pimg
|
||||
|
||||
if args.output.endswith(".h"):
|
||||
ofile = BinHeader(args.output, arrayname=friendly_out_name)
|
||||
else:
|
||||
ofile = open(args.output, "wb")
|
||||
|
||||
for y in range(0, img.height - (tsize_y - 1), tsize_y):
|
||||
for x in range(0, img.width - (tsize_x - 1), tsize_x):
|
||||
tile = img.crop((x, y, x + tsize_x, y + tsize_y))
|
||||
ofile.write(bytes(bytes_from_bitstream_le(
|
||||
format_pixel(args.format, image_is_transparent, tile.getpixel((i, j)), args.dither, dithercoord=(i, j)) for j in range(tsize_y) for i in range(tsize_x)
|
||||
)))
|
||||
if args.metadata:
|
||||
assert(tsize_x * tsize_y % 4 == 0)
|
||||
for y in range(0, tsize_y):
|
||||
opacity = list(img.getpixel((x, y))[3] >= 128 for x in range(tsize_x))
|
||||
try:
|
||||
first_transparent = opacity.index(True)
|
||||
last_transparent = tsize_x - 1 - list(reversed(opacity)).index(True)
|
||||
continuous_span = all(opacity[first_transparent:last_transparent + 1])
|
||||
ofile.write(struct.pack("<L", (last_transparent & 0xffff) | ((first_transparent & 0x7fff) << 16) | ((continuous_span & 1) << 31)))
|
||||
except ValueError:
|
||||
# Completely transparent row
|
||||
ofile.write(struct.pack("<L", 0))
|
||||
|
||||
ofile.close()
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Raspberry Pi iceprog (piceprog)
|
||||
# Tool for programming iCE40 FPGAs with Pi GPIOs
|
||||
|
||||
import sys
|
||||
import RPi.GPIO as gpio
|
||||
from time import sleep
|
||||
from math import ceil
|
||||
|
||||
fpga_sclk = 19
|
||||
fpga_sdi = 26
|
||||
fpga_ss = 13
|
||||
fpga_done = 6
|
||||
fpga_rst = 5
|
||||
|
||||
gpio.setmode(gpio.BCM)
|
||||
gpio.setwarnings(False)
|
||||
|
||||
for pin in [fpga_sclk, fpga_sdi, fpga_ss, fpga_rst]:
|
||||
gpio.setup(pin, gpio.OUT)
|
||||
|
||||
gpio.setup(fpga_done, gpio.IN)
|
||||
|
||||
def prog(fname):
|
||||
bitstream = list(open(fname, "rb").read())
|
||||
print("{} bytes.".format(len(bitstream)))
|
||||
bits = []
|
||||
for byte in bitstream:
|
||||
for i in range(8):
|
||||
bits.append(byte >> 7)
|
||||
byte = (byte << 1) & 0xff
|
||||
bits.extend(49 * [0]) # at least 49 dummy cycles required at end.
|
||||
print("Starting")
|
||||
gpio.output(fpga_rst, 0)
|
||||
gpio.output(fpga_ss, 0)
|
||||
gpio.output(fpga_sclk, 1) # CPOL = 1 (clock idle high)
|
||||
gpio.output(fpga_sdi, 0)
|
||||
sleep(0.001)
|
||||
gpio.output(fpga_rst, 1)
|
||||
sleep(0.001)
|
||||
gpio.output(fpga_ss, 1)
|
||||
for i in range(8):
|
||||
gpio.output(fpga_sclk, 0)
|
||||
gpio.output(fpga_sclk, 1)
|
||||
# CPHA = 1 (data captured on trailing edge of clock pulse)
|
||||
gpio.output(fpga_ss, 0)
|
||||
for bit in bits:
|
||||
gpio.output(fpga_sdi, bit)
|
||||
gpio.output(fpga_sclk, 0)
|
||||
gpio.output(fpga_sclk, 1)
|
||||
if gpio.input(fpga_done):
|
||||
print("CDONE high, yay!")
|
||||
else:
|
||||
print("CDONE not high, something may have gone wrong")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
exit("Usage: piceprog (file.bin)")
|
||||
prog(sys.argv[1])
|
||||
+526
@@ -0,0 +1,526 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Regblock generation script
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
import textwrap
|
||||
import yaml
|
||||
from collections import OrderedDict
|
||||
from math import log2, ceil
|
||||
|
||||
# Regblock data structures and parser/loader
|
||||
|
||||
class RegBlock:
|
||||
bus_types = ["apb"]
|
||||
field_properties = ["name", "b", "access", "info", "rst", "concat"]
|
||||
def __init__(self, name, w_data, w_addr, bus, info):
|
||||
self.name = name
|
||||
self.w_data = w_data
|
||||
self.w_addr = w_addr
|
||||
if bus not in self.bus_types:
|
||||
raise Exception("Unknown bus type: {}".format(bus))
|
||||
self.bus = bus
|
||||
self.info = info
|
||||
self.regs = []
|
||||
|
||||
def add(self, reg):
|
||||
self.regs.append(reg)
|
||||
|
||||
@staticmethod
|
||||
def load(file):
|
||||
y = yaml.load(file.read(), Loader=yaml.FullLoader)
|
||||
if "bus" not in y:
|
||||
sys.exit("Must specify bus type with \"bus: x\"")
|
||||
if "data" not in y:
|
||||
sys.exit("Must specify data width with \"data: x\"")
|
||||
if "addr" not in y:
|
||||
sys.exit("Must specify address width with \"addr: x\"")
|
||||
if "name" not in y:
|
||||
sys.exit("Must specify regblock name with \"name: x\"")
|
||||
rb = RegBlock(y["name"], y["data"], y["addr"], y["bus"], y["info"] if "info" in y else None)
|
||||
param_dict = {"W_DATA": y["data"]}
|
||||
if ("params" in y):
|
||||
param_dict = {**param_dict, **y["params"]}
|
||||
param_resolve = lambda x: x if type(x) is int else int(eval(x, {**param_dict}))
|
||||
# Expand any generate blocks (one level only)
|
||||
reglist = []
|
||||
for i, rspec in enumerate(y["regs"]):
|
||||
if "generate" not in rspec:
|
||||
reglist.append(rspec)
|
||||
continue
|
||||
yaml_lines = []
|
||||
emit = lambda x: yaml_lines.append(str(x))
|
||||
exec(rspec["generate"], {"_": emit, **param_dict}, dict())
|
||||
newregs = yaml.load("\n".join(yaml_lines), Loader=yaml.FullLoader)
|
||||
if newregs is not None:
|
||||
reglist.extend(newregs)
|
||||
# Then process the expanded reglist
|
||||
for rspec in reglist:
|
||||
reg = Register(rspec["name"], rb.w_data, rspec["info"] if "info" in rspec else None, rspec["wstb"] if "wstb" in rspec else None)
|
||||
rb.add(reg)
|
||||
for fspec in rspec["bits"]:
|
||||
for key in fspec:
|
||||
if key not in RegBlock.field_properties:
|
||||
raise Exception("'{}' is not a valid property for a field.".format(key))
|
||||
bitrange = fspec["b"]
|
||||
if type(bitrange) is int:
|
||||
bitrange = [bitrange, bitrange]
|
||||
reg.add(Field(
|
||||
fspec["name"] if "name" in fspec else "",
|
||||
param_resolve(bitrange[0]),
|
||||
param_resolve(bitrange[1]),
|
||||
fspec["access"],
|
||||
fspec["rst"] if "rst" in fspec else 0,
|
||||
fspec["info"] if "info" in fspec else None,
|
||||
fspec["concat"] if "concat" in fspec else None
|
||||
))
|
||||
return rb
|
||||
|
||||
def accept(self, visitor):
|
||||
visitor.pre(self)
|
||||
for reg in self.regs:
|
||||
reg.accept(visitor)
|
||||
visitor.post(self)
|
||||
|
||||
class Register:
|
||||
def __init__(self, name, width, info, wstrobe=None):
|
||||
assert(width > 0)
|
||||
self.name = name
|
||||
self.width = width
|
||||
self.info = info
|
||||
self.occupancy = [None] * width
|
||||
self.fields = OrderedDict()
|
||||
self.wstrobe = wstrobe
|
||||
|
||||
def add(self, field):
|
||||
if field.lsb >= self.width or field.msb >= self.width or field.lsb < 0 or field.msb < 0:
|
||||
raise Exception("Field {} extends outside of register {}".format(field.name, self.name))
|
||||
if field.name in self.fields:
|
||||
raise Exception("{} already has a field called \"{}\"".format(self.name, field.name))
|
||||
for i in range(field.lsb, field.msb + 1):
|
||||
if self.occupancy[i] is not None:
|
||||
raise Exception("Field {} overlaps {} in register {}".format(field.name, self.occupancy[i], self.name))
|
||||
self.occupancy[i] = field.name
|
||||
self.fields[field.name] = field
|
||||
field.parent = self
|
||||
|
||||
def accept(self, visitor):
|
||||
visitor.pre(self)
|
||||
for field in self.fields.values():
|
||||
field.accept(visitor)
|
||||
visitor.post(self)
|
||||
|
||||
|
||||
class Field:
|
||||
access_types = ["ro", "rov", "wo", "rw", "rf", "wf", "rwf", "sc", "w1c"]
|
||||
def __init__(self, name, msb, lsb, access, resetval=0, info="", concat=None, parent=None):
|
||||
if lsb > msb:
|
||||
raise Exception("Field width must be >= 0 in field {}".format(name))
|
||||
if access not in self.access_types:
|
||||
raise Exception("Unknown access type: {}. Recognised types: {}".format(access, ", ".join(self.access_types)))
|
||||
if access in ["sc", "w1c"] and msb != lsb:
|
||||
raise Exception("Field width must be 1 for access type '{}'".format(access))
|
||||
self.name = name
|
||||
self.msb = msb
|
||||
self.lsb = lsb
|
||||
self.access = access
|
||||
self.resetval = resetval
|
||||
self.info = info
|
||||
self.concat = concat
|
||||
self.parent = parent
|
||||
|
||||
@property
|
||||
def width_decl(self):
|
||||
if self.msb == self.lsb:
|
||||
return ""
|
||||
else:
|
||||
return "[{}:0]".format(self.msb - self.lsb)
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
return self.msb - self.lsb + 1
|
||||
|
||||
@property
|
||||
def fullname(self):
|
||||
if self.name == "":
|
||||
return self.parent.name
|
||||
else:
|
||||
return "{}_{}".format(self.parent.name, self.name)
|
||||
|
||||
def accept(self, visitor):
|
||||
visitor.pre(self)
|
||||
|
||||
# Base class for various useful lumps of functionality
|
||||
# which are applied as traversals on a regblock description tree
|
||||
class RegBlockVisitor:
|
||||
def pre(self, x):
|
||||
if type(x) is RegBlock:
|
||||
self.pre_regblock(x)
|
||||
elif type(x) is Register:
|
||||
self.pre_register(x)
|
||||
elif type(x) is Field:
|
||||
self.pre_field(x)
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
def post(self, x):
|
||||
if type(x) is RegBlock:
|
||||
self.post_regblock(x)
|
||||
elif type(x) is Register:
|
||||
self.post_register(x)
|
||||
elif type(x) is Field:
|
||||
self.post_field(x)
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
def pre_regblock(self, rb):
|
||||
raise NotImplementedError()
|
||||
|
||||
def pre_register(self, r):
|
||||
raise NotImplementedError()
|
||||
|
||||
def pre_field(self, f):
|
||||
raise NotImplementedError()
|
||||
|
||||
def post_regblock(self, rb):
|
||||
raise NotImplementedError()
|
||||
|
||||
def post_register(self, r):
|
||||
raise NotImplementedError()
|
||||
|
||||
def post_field(self, f):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
# Verilog generation
|
||||
|
||||
def width2str(w):
|
||||
return "[{}:0]".format(w - 1)
|
||||
|
||||
def c_block_comment(lines, width=80, align="<"):
|
||||
blines = []
|
||||
blines.append("/" + (width - 1) * "*")
|
||||
line_fmt = "* {:" + align + str(width - 4) + "} *"
|
||||
for l in lines:
|
||||
blines.append(line_fmt.format(l.strip()))
|
||||
blines.append((width - 1) * "*" + "/")
|
||||
return blines
|
||||
|
||||
def c_line_comment(line):
|
||||
if not hasattr(c_line_comment, "wrap"):
|
||||
c_line_comment.wrap = textwrap.TextWrapper(width=80, initial_indent="// ", subsequent_indent="// ")
|
||||
return "\n".join(c_line_comment.wrap.wrap(line.strip()))
|
||||
|
||||
class Verilog:
|
||||
def __init__(self):
|
||||
self.header = []
|
||||
self.ports = []
|
||||
self.decls = []
|
||||
self.logic_comb = []
|
||||
self.logic_rst = []
|
||||
self.logic_clk = []
|
||||
|
||||
def __add__(self, other):
|
||||
new = Verilog()
|
||||
new.header.extend(self.header)
|
||||
for n, s, o in zip(
|
||||
[new.ports, new.decls, new.logic_comb, new.logic_rst, new.logic_clk],
|
||||
[self.ports, self.decls, self.logic_comb, self.logic_rst, self.logic_clk],
|
||||
[other.ports, other.decls, other.logic_comb, other.logic_rst, other.logic_clk]):
|
||||
n.extend(s)
|
||||
n.extend(o)
|
||||
return new
|
||||
|
||||
def __str__(self):
|
||||
strs = []
|
||||
strs.extend(self.header)
|
||||
strs.extend("\t" + s + ("" if s == "" or s.startswith("//") else ",") for s in self.ports[:-1])
|
||||
if len(self.ports) > 0:
|
||||
strs.append("\t" + self.ports[-1])
|
||||
strs.append(");")
|
||||
strs.append("")
|
||||
strs.extend(self.decls);
|
||||
strs.append("")
|
||||
strs.append("always @ (*) begin")
|
||||
strs.extend("\t" + s for s in self.logic_comb)
|
||||
strs.append("end")
|
||||
strs.append("")
|
||||
strs.append("always @ (posedge clk or negedge rst_n) begin")
|
||||
strs.append("\tif (!rst_n) begin")
|
||||
strs.extend("\t\t" + s for s in self.logic_rst)
|
||||
strs.append("\tend else begin")
|
||||
strs.extend("\t\t" + s for s in self.logic_clk)
|
||||
strs.append("\tend")
|
||||
strs.append("end")
|
||||
strs.append("")
|
||||
strs.append("endmodule\n")
|
||||
return "\n".join(strs)
|
||||
|
||||
|
||||
class VerilogWriter(RegBlockVisitor):
|
||||
def __init__(self):
|
||||
self.v = Verilog()
|
||||
self.regname = None
|
||||
self.concat = OrderedDict()
|
||||
self.wstrobe = OrderedDict()
|
||||
|
||||
def pre_regblock(self, rb):
|
||||
v = self.v
|
||||
v.header.extend(c_block_comment(["AUTOGENERATED BY REGBLOCK", "Do not edit manually.",
|
||||
"Edit the source file (or regblock utility) and regenerate."], align = "^"))
|
||||
v.header.append("")
|
||||
v.header.append(c_line_comment("{:<20} : {}".format("Block name", rb.name)))
|
||||
v.header.append(c_line_comment("{:<20} : {}".format("Bus type", rb.bus)))
|
||||
v.header.append(c_line_comment("{:<20} : {}".format("Bus data width", rb.w_data)))
|
||||
v.header.append(c_line_comment("{:<20} : {}".format("Bus address width", rb.w_addr)))
|
||||
v.header.append("")
|
||||
if rb.info is not None:
|
||||
v.header.append(c_line_comment(rb.info))
|
||||
v.header.append("")
|
||||
v.header.append("module {}_regs (".format(rb.name))
|
||||
v.ports.extend(["input wire clk", "input wire rst_n",])
|
||||
addr_mask = (1 << 2 + ceil(log2(len(rb.regs)))) - 1
|
||||
addr_mask = addr_mask & (-1 << ceil(log2(rb.w_data / 8)))
|
||||
if rb.bus == "apb":
|
||||
v.ports.append("")
|
||||
v.ports.append("// APB Port")
|
||||
v.ports.append("input wire apbs_psel")
|
||||
v.ports.append("input wire apbs_penable")
|
||||
v.ports.append("input wire apbs_pwrite")
|
||||
v.ports.append("input wire {} apbs_paddr".format(width2str(rb.w_addr)))
|
||||
v.ports.append("input wire {} apbs_pwdata".format(width2str(rb.w_data)))
|
||||
v.ports.append("output wire {} apbs_prdata".format(width2str(rb.w_data)))
|
||||
v.ports.append("output wire apbs_pready")
|
||||
v.ports.append("output wire apbs_pslverr")
|
||||
v.decls.append("// APB adapter")
|
||||
v.decls.append("wire {} wdata = apbs_pwdata;".format(width2str(rb.w_data)))
|
||||
v.decls.append("reg {} rdata;".format(width2str(rb.w_data)))
|
||||
v.decls.append("wire wen = apbs_psel && apbs_penable && apbs_pwrite;")
|
||||
v.decls.append("wire ren = apbs_psel && apbs_penable && !apbs_pwrite;")
|
||||
v.decls.append("wire {} addr = apbs_paddr & {}'h{:x};".format(width2str(rb.w_addr), rb.w_addr, addr_mask))
|
||||
v.decls.append("assign apbs_prdata = rdata;")
|
||||
v.decls.append("assign apbs_pready = 1'b1;")
|
||||
v.decls.append("assign apbs_pslverr = 1'b0;")
|
||||
v.decls.append("")
|
||||
v.ports.append("")
|
||||
v.ports.append("// Register interfaces")
|
||||
for i, reg in enumerate(rb.regs):
|
||||
v.decls.append("localparam ADDR_{} = {};".format(reg.name.upper(), i * 4))
|
||||
v.decls.append("")
|
||||
for reg in rb.regs:
|
||||
v.decls.append("wire __{}_wen = wen && addr == ADDR_{};".format(reg.name, reg.name.upper()))
|
||||
v.decls.append("wire __{}_ren = ren && addr == ADDR_{};".format(reg.name, reg.name.upper()))
|
||||
v.logic_comb.append("case (addr)")
|
||||
for reg in rb.regs:
|
||||
v.logic_comb.append("\tADDR_{}: rdata = __{}_rdata;".format(reg.name.upper(), reg.name))
|
||||
v.logic_comb.append("\tdefault: rdata = {}'h0;".format(rb.w_data))
|
||||
v.logic_comb.append("endcase")
|
||||
|
||||
def post_regblock(self, rb):
|
||||
for name, conns in self.concat.items():
|
||||
concat_name = "concat_{}_o".format(name)
|
||||
width = sum(f[0] for f in conns)
|
||||
self.v.ports.append("output wire [{}:0] {}".format(width - 1, concat_name))
|
||||
self.v.decls.append("assign {} = {{{}}};".format(concat_name, ", ".join(f[1] for f in reversed(conns))))
|
||||
max_strobe_index = OrderedDict()
|
||||
for name, conns in self.wstrobe.items():
|
||||
self.v.logic_rst.append("wstrobe_{} <= 1'b0;".format(name))
|
||||
self.v.logic_clk.append("wstrobe_{} <= {};".format(name, " || ".join(
|
||||
"__{}_wen".format(conn) for conn in conns)))
|
||||
if name.endswith("]"):
|
||||
shortname = name.split("[")[0]
|
||||
idx = int(name.split("[")[-1][:-1])
|
||||
if shortname in max_strobe_index:
|
||||
max_strobe_index[shortname] = max(max_strobe_index[shortname], idx)
|
||||
else:
|
||||
max_strobe_index[shortname] = idx
|
||||
else:
|
||||
self.v.ports.append("output reg wstrobe_{}".format(name))
|
||||
for name, max_idx in max_strobe_index.items():
|
||||
self.v.ports.append("output reg [{}:0] wstrobe_{}".format(max_idx, name))
|
||||
|
||||
def pre_register(self, reg):
|
||||
v = self.v
|
||||
self.regname = reg.name
|
||||
v.decls.append("")
|
||||
rdata_conns = []
|
||||
empty_count = 0
|
||||
last_occupant = None
|
||||
for occupant in reversed(reg.occupancy):
|
||||
if occupant is None:
|
||||
empty_count += 1
|
||||
elif occupant != last_occupant:
|
||||
if empty_count > 0:
|
||||
rdata_conns.append("{}'h0".format(empty_count))
|
||||
empty_count = 0
|
||||
rdata_conns.append("{}_rdata".format(reg.fields[occupant].fullname))
|
||||
last_occupant = occupant
|
||||
if empty_count > 0:
|
||||
rdata_conns.append("{}'h0".format(empty_count))
|
||||
for field in reg.fields.values():
|
||||
lsb = reg.occupancy.index(field.name)
|
||||
msb = lsb - 1 + reg.occupancy.count(field.name)
|
||||
index = "[{}]".format(lsb) if msb == lsb else "[{}:{}]".format(msb, lsb)
|
||||
v.decls.append("wire {} {}_wdata = wdata{};".format(field.width_decl, field.fullname, index))
|
||||
v.decls.append("wire {} {}_rdata;".format(field.width_decl, field.fullname))
|
||||
v.decls.append("wire {} __{}_rdata = {{{}}};".format(width2str(reg.width), reg.name, ", ".join(rdata_conns)))
|
||||
|
||||
if reg.wstrobe is not None:
|
||||
if not reg.wstrobe in self.wstrobe:
|
||||
self.wstrobe[reg.wstrobe] = []
|
||||
self.wstrobe[reg.wstrobe].append(reg.name)
|
||||
|
||||
def post_register(self, reg):
|
||||
pass
|
||||
|
||||
def pre_field(self, f):
|
||||
v = self.v
|
||||
rname = self.regname
|
||||
fname = f.fullname
|
||||
if f.access in ["rov", "rf", "rwf", "w1c"]:
|
||||
v.ports.append("input wire {} {}_i".format(f.width_decl, fname))
|
||||
if f.access in ["rov", "rf", "rwf"]:
|
||||
v.decls.append("assign {}_rdata = {}_i;".format(fname, fname))
|
||||
if f.access in ["wo", "rw", "wf", "rwf", "sc", "w1c"]:
|
||||
v.ports.append("output reg {} {}_o".format(f.width_decl, fname))
|
||||
if f.access in ["wf", "rwf"]:
|
||||
v.ports.append("output reg {}_wen".format(fname))
|
||||
v.logic_comb.append("{}_wen = __{}_wen;".format(fname, rname))
|
||||
v.logic_comb.append("{}_o = {}_wdata;".format(fname, fname))
|
||||
if f.access in ["rf", "rwf"]:
|
||||
v.ports.append("output reg {}_ren".format(fname))
|
||||
v.logic_comb.append("{}_ren = __{}_ren;".format(fname, rname))
|
||||
if f.access in ["rw"]:
|
||||
v.decls.append("assign {}_rdata = {}_o;".format(fname, fname))
|
||||
if f.access in ["rw", "wo"]:
|
||||
v.logic_rst.append("{}_o <= {}'h{:x};".format(fname, f.width, f.resetval))
|
||||
v.logic_clk.append("if (__{}_wen)".format(rname))
|
||||
v.logic_clk.append("\t{}_o <= {}_wdata;".format(fname, fname))
|
||||
if f.access in ["w1c"]:
|
||||
v.logic_rst.append("{} <= {}'h{:x};".format(fname, f.width, f.resetval))
|
||||
v.decls.append("reg {} {};".format(f.width_decl, fname))
|
||||
v.decls.append("assign {}_rdata = {};".format(fname, fname))
|
||||
v.logic_clk.append("{0} <= ({0} && !(__{1}_wen && {0}_wdata)) || {0}_i;".format(fname, rname))
|
||||
v.logic_comb.append("{0}_o = {0};".format(fname))
|
||||
if f.access in ["sc"]:
|
||||
v.logic_comb.append("{0}_o = {0}_wdata & {{{1}{{__{2}_wen}}}};".format(fname, f.width, rname))
|
||||
if f.access in ["ro", "wo", "wf", "sc"]:
|
||||
v.decls.append("assign {}_rdata = {}'h{:x};".format(fname, f.width, f.resetval))
|
||||
|
||||
if f.concat is not None:
|
||||
if not f.access in ["wo", "rw", "wf", "rwf", "sc"]:
|
||||
raise Exception("concat specified for port with no regblock output")
|
||||
if not f.concat in self.concat:
|
||||
self.concat[f.concat] = []
|
||||
self.concat[f.concat].append((f.width, "{}_o".format(fname)))
|
||||
|
||||
|
||||
# C header generation
|
||||
|
||||
class HeaderWriter(RegBlockVisitor):
|
||||
def __init__(self):
|
||||
self.lines = []
|
||||
self.blockname = None
|
||||
self.regname = None
|
||||
|
||||
def __str__(self):
|
||||
return "".join(l + "\n" for l in self.lines)
|
||||
|
||||
def pre_regblock(self, rb):
|
||||
lines = self.lines
|
||||
self.blockname = rb.name
|
||||
lines.extend(c_block_comment(["AUTOGENERATED BY REGBLOCK", "Do not edit manually.",
|
||||
"Edit the source file (or regblock utility) and regenerate."], align = "^"))
|
||||
lines.append("")
|
||||
lines.append("#ifndef _{}_REGS_H_".format(rb.name.upper()))
|
||||
lines.append("#define _{}_REGS_H_".format(rb.name.upper()))
|
||||
lines.append("")
|
||||
lines.append(c_line_comment("{:<20} : {}".format("Block name", rb.name)))
|
||||
lines.append(c_line_comment("{:<20} : {}".format("Bus type", rb.bus)))
|
||||
lines.append(c_line_comment("{:<20} : {}".format("Bus data width", rb.w_data)))
|
||||
lines.append(c_line_comment("{:<20} : {}".format("Bus address width", rb.w_addr)))
|
||||
lines.append("")
|
||||
if rb.info is not None:
|
||||
lines.append(c_line_comment(rb.info))
|
||||
lines.append("")
|
||||
for i, reg in enumerate(rb.regs):
|
||||
lines.append("#define {}_{}_OFFS {}".format(rb.name.upper(), reg.name.upper(), i * 4))
|
||||
|
||||
def post_regblock(self, rb):
|
||||
self.lines.append("")
|
||||
self.lines.append("#endif // _{}_REGS_H_".format(rb.name.upper()))
|
||||
|
||||
def pre_register(self, reg):
|
||||
lines = self.lines
|
||||
lines.append("")
|
||||
lines.extend(c_block_comment([reg.name.upper()], align = "^"))
|
||||
lines.append("")
|
||||
if reg.info is not None:
|
||||
lines.append(c_line_comment(reg.info))
|
||||
lines.append("")
|
||||
self.regname = reg.name
|
||||
|
||||
def post_register(self, reg):
|
||||
pass
|
||||
|
||||
def pre_field(self, f):
|
||||
fname = f.fullname.upper()
|
||||
lines = self.lines
|
||||
lines.append(c_line_comment("Field: {} Access: {}".format(fname, f.access.upper())))
|
||||
fname = self.blockname.upper() + "_" + fname
|
||||
if f.info is not None:
|
||||
lines.append(c_line_comment(f.info))
|
||||
lines.append("#define {}_LSB {}".format(fname, f.lsb))
|
||||
lines.append("#define {}_BITS {}".format(fname, f.width))
|
||||
mask = 0
|
||||
for i in range(32):
|
||||
if i in range(f.lsb, f.msb + 1):
|
||||
mask = mask | (1 << i)
|
||||
lines.append("#define {}_MASK {:#x}".format(fname, mask))
|
||||
|
||||
def change_ext(fname, ext):
|
||||
return ".".join(fname.split(".")[0:-1] + [ext.strip(".")])
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("src", help="Source file to read from (pass - for stdin)")
|
||||
parser.add_argument("--verilog", "-v", help="Verilog file to write to (pass - for stdout)")
|
||||
parser.add_argument("--cheader", "-c", help="C header file to write to (pass - for stdout)")
|
||||
parser.add_argument("--all", "-a", action="store_true", help="Generate all output types, with default filenames")
|
||||
args = parser.parse_args()
|
||||
sfile = None
|
||||
vfile = None
|
||||
hfile = None
|
||||
if args.src == "-" and args.all:
|
||||
exit("Cannot use --all with stdin input")
|
||||
if args.src == "-":
|
||||
sfile = sys.stdin
|
||||
else:
|
||||
sfile = open(args.src)
|
||||
rb = RegBlock.load(sfile)
|
||||
|
||||
if args.verilog is not None or args.all:
|
||||
if args.verilog == "-":
|
||||
vfile = sys.stdout
|
||||
elif args.verilog is None:
|
||||
vfile = open(change_ext(args.src, ".v"), "w")
|
||||
else:
|
||||
vfile = open(args.verilog, "w")
|
||||
if args.cheader is not None or args.all:
|
||||
if (args.cheader == "-"):
|
||||
hfile = sys.stdout
|
||||
elif args.verilog is None:
|
||||
hfile = open(change_ext(args.src, ".h"), "w")
|
||||
else:
|
||||
hfile = open(args.cheader, "w")
|
||||
|
||||
if vfile is not None:
|
||||
vw = VerilogWriter()
|
||||
rb.accept(vw)
|
||||
vfile.write(str(vw.v))
|
||||
if hfile is not None:
|
||||
hw = HeaderWriter()
|
||||
rb.accept(hw)
|
||||
hfile.write(str(hw))
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
TOP ?= tb
|
||||
DOTF ?= $(TOP).f
|
||||
SIMNAME?=simulation
|
||||
|
||||
SIM_VARS = PLATFORM=lin64 LD_LIBRARY_PATH=$XILINX/lib/$PLATFORM
|
||||
FUSE ?= $(SIM_VARS) fuse
|
||||
SIMSCRIPT ?= $(SCRIPTS)/sim_run.tcl
|
||||
GUISCRIPT ?= $(SCRIPTS)/gui_run.tcl
|
||||
|
||||
# Kill implicit rules
|
||||
.SUFFIXES:
|
||||
.IMPLICIT:
|
||||
|
||||
sim: build
|
||||
(cd sim; $(SIM_VARS) ./$(SIMNAME) -tclbatch $(SIMSCRIPT))
|
||||
|
||||
gui: build
|
||||
(cd sim; $(SIM_VARS) ./$(SIMNAME) -gui -tclbatch $(GUISCRIPT))
|
||||
|
||||
build:
|
||||
mkdir -p sim
|
||||
$(SCRIPTS)/listfiles --relativeto sim -f isim $(DOTF) -o sim/sim.prj
|
||||
(cd sim; $(FUSE) -d SIM -prj sim.prj $(TOP) -o $(SIMNAME))
|
||||
|
||||
clean::
|
||||
rm -rf sim
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
run 1s;
|
||||
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
SRCS ?= $(wildcard *.c) $(wildcard *.S)
|
||||
APPNAME ?= test
|
||||
|
||||
OBJS = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SRCS)))
|
||||
|
||||
CROSS_PREFIX=riscv32-unknown-elf-
|
||||
CC=$(CROSS_PREFIX)gcc
|
||||
LD=$(CROSS_PREFIX)gcc
|
||||
OBJCOPY=$(CROSS_PREFIX)objcopy
|
||||
OBJDUMP=$(CROSS_PREFIX)objdump
|
||||
|
||||
MARCH?=rv32ic
|
||||
LDSCRIPT?=memmap.ld
|
||||
override CCFLAGS+=-c -march=$(MARCH) $(addprefix -I ,$(INCDIRS))
|
||||
override CCFLAGS+=-Wall -Wextra -Wno-parentheses
|
||||
override LDFLAGS+=-march=$(MARCH) -T $(LDSCRIPT)
|
||||
|
||||
# Override to -D to get all sections
|
||||
DISASSEMBLE?=-d
|
||||
|
||||
.SUFFIXES:
|
||||
.SECONDARY:
|
||||
.PHONY: all clean
|
||||
all: compile
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CCFLAGS) $< -o $@
|
||||
|
||||
%.o: %.S
|
||||
$(CC) $(CCFLAGS) $< -o $@
|
||||
|
||||
$(APPNAME).elf: $(OBJS)
|
||||
$(LD) $(LDFLAGS) $(OBJS) $(addprefix -l,$(LIBS)) -o $(APPNAME).elf
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -O binary $< $@
|
||||
|
||||
%8.hex: %.elf
|
||||
$(OBJCOPY) -O verilog $< $@
|
||||
|
||||
%32.hex: %8.hex
|
||||
$(SCRIPTS)/vhexwidth -w 32 $< -o $@
|
||||
|
||||
$(APPNAME).dis: $(APPNAME).elf
|
||||
@echo ">>>>>>>>> Memory map:" > $(APPNAME).dis
|
||||
$(OBJDUMP) -h $(APPNAME).elf >> $(APPNAME).dis
|
||||
@echo >> $(APPNAME).dis
|
||||
@echo ">>>>>>>>> Disassembly:" >> $(APPNAME).dis
|
||||
$(OBJDUMP) $(DISASSEMBLE) $(APPNAME).elf >> $(APPNAME).dis
|
||||
|
||||
|
||||
compile:: $(APPNAME)32.hex $(APPNAME).dis $(APPNAME).bin
|
||||
|
||||
clean::
|
||||
rm -f $(APPNAME).elf $(APPNAME)32.hex $(APPNAME)8.hex $(APPNAME).dis $(APPNAME).bin $(OBJS)
|
||||
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
YOSYS=yosys
|
||||
NEXTPNR=nextpnr-ecp5
|
||||
TRELLIS?=/usr/share/trellis
|
||||
|
||||
CHIPNAME?=chip
|
||||
DEVICE?=um5g-85k
|
||||
PACKAGE?=CABGA381
|
||||
DEVICE_IDCODE?=0x41113043
|
||||
|
||||
DEFINES+=FPGA FPGA_ECP5
|
||||
|
||||
SYNTH_OPT?=
|
||||
PNR_OPT?=
|
||||
|
||||
SYNTH_CMD=read_verilog $(addprefix -I,$(INCDIRS)) $(addprefix -D,$(DEFINES)) $(SRCS);
|
||||
ifneq (,$(TOP))
|
||||
SYNTH_CMD+=hierarchy -top $(TOP);
|
||||
endif
|
||||
SYNTH_CMD+=synth_ecp5 $(SYNTH_OPT) -json $(CHIPNAME).json
|
||||
|
||||
# Kill implicit rules
|
||||
.SUFFIXES:
|
||||
.IMPLICIT:
|
||||
|
||||
.PHONY: all romfiles synth clean program dump
|
||||
|
||||
all: bit
|
||||
|
||||
romfiles::
|
||||
synth: romfiles $(CHIPNAME).json
|
||||
dump: romfiles
|
||||
pnr: synth $(CHIPNAME).config
|
||||
bit: pnr $(CHIPNAME).bit $(CHIPNAME).svf
|
||||
|
||||
SRCS=$(shell PROJ_ROOT=$(PROJ_ROOT) HDL=$(HDL) $(SCRIPTS)/listfiles --relative -f flat $(DOTF))
|
||||
INCDIRS=$(shell PROJ_ROOT=$(PROJ_ROOT) HDL=$(HDL) $(SCRIPTS)/listfiles --relative -f flati $(DOTF))
|
||||
|
||||
dump:
|
||||
$(YOSYS) -p "$(SYNTH_CMD); write_verilog $(CHIPNAME)_synth.v"
|
||||
|
||||
$(CHIPNAME).json: $(SRCS)
|
||||
@echo ">>> Synth"
|
||||
@echo
|
||||
$(YOSYS) -p "$(SYNTH_CMD)" > synth.log
|
||||
tail -n 35 synth.log
|
||||
|
||||
|
||||
$(CHIPNAME).config: $(CHIPNAME).json $(CHIPNAME).lpf
|
||||
@echo ">>> Place and Route"
|
||||
@echo
|
||||
$(NEXTPNR) -r --placer sa --$(DEVICE) --package $(PACKAGE) --lpf $(CHIPNAME).lpf --json $(CHIPNAME).json --textcfg $@ $(PNR_OPT) --quiet --log pnr.log
|
||||
@grep "Info: Max frequency for clock " pnr.log | tail -n 1
|
||||
|
||||
$(CHIPNAME).bit: $(CHIPNAME).config
|
||||
@echo ">>> Generate Bitstream"
|
||||
@echo
|
||||
ecppack --compress --svf $(CHIPNAME).svf --idcode $(DEVICE_IDCODE) $< $@
|
||||
|
||||
$(CHIPNAME).svf: $(CHIPNAME).bit
|
||||
|
||||
clean::
|
||||
rm -f $(CHIPNAME).json $(CHIPNAME).asc $(CHIPNAME).bit $(CHIPNAME)_synth.v
|
||||
rm -f synth.log pnr.log
|
||||
|
||||
# Code for trying n different pnr seeds and reporting results
|
||||
|
||||
PNR_N_TRIES := 100
|
||||
PNR_TRY_LIST := $(shell seq $(PNR_N_TRIES))
|
||||
|
||||
pnr_sweep: $(addprefix pnr_try,$(PNR_TRY_LIST))
|
||||
|
||||
define make-sweep-target
|
||||
pnr_try$1: synth
|
||||
@echo ">>> Starting sweep $1"
|
||||
$(NEXTPNR) --seed $1 --placer sa --$(DEVICE) --package $(PACKAGE) --lpf $(CHIPNAME).lpf --json $(CHIPNAME).json --textcfg pnr_try$1.config $(PNR_OPT) --quiet --log pnr$1.log
|
||||
@grep "Info: Max frequency for clock " pnr$1.log | tail -n 1
|
||||
endef
|
||||
|
||||
$(foreach try,$(PNR_TRY_LIST),$(eval $(call make-sweep-target,$(try))))
|
||||
|
||||
clean::
|
||||
rm -f pnr_try*.asc pnr*.log
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
YOSYS=yosys
|
||||
NEXTPNR=nextpnr-ice40
|
||||
CHIPNAME?=chip
|
||||
DEVICE?=hx8k
|
||||
PACKAGE?=bg121
|
||||
|
||||
DEFINES+=FPGA FPGA_ICE40
|
||||
|
||||
PRE_SYNTH_CMD?=
|
||||
SYNTH_OPT?=
|
||||
PNR_OPT?=
|
||||
|
||||
SYNTH_CMD=read_verilog $(addprefix -I,$(INCDIRS)) $(addprefix -D,$(DEFINES)) $(SRCS);
|
||||
ifneq (,$(TOP))
|
||||
SYNTH_CMD+=hierarchy -top $(TOP);
|
||||
endif
|
||||
SYNTH_CMD+=$(PRE_SYNTH_CMD)
|
||||
SYNTH_CMD+=synth_ice40 $(SYNTH_OPT); write_json $(CHIPNAME).json
|
||||
|
||||
# Kill implicit rules
|
||||
.SUFFIXES:
|
||||
.IMPLICIT:
|
||||
|
||||
.PHONY: all romfiles synth clean program dump
|
||||
|
||||
all: bit
|
||||
|
||||
romfiles::
|
||||
synth: romfiles $(CHIPNAME).json
|
||||
dump: romfiles
|
||||
pnr: synth $(CHIPNAME).asc
|
||||
bit: pnr $(CHIPNAME).bin
|
||||
|
||||
SRCS=$(shell PROJ_ROOT=$(PROJ_ROOT) HDL=$(HDL) $(SCRIPTS)/listfiles --relative -f flat $(DOTF))
|
||||
INCDIRS=$(shell PROJ_ROOT=$(PROJ_ROOT) HDL=$(HDL) $(SCRIPTS)/listfiles --relative -f flati $(DOTF))
|
||||
|
||||
dump:
|
||||
$(YOSYS) -p "$(SYNTH_CMD); write_verilog $(CHIPNAME)_synth.v"
|
||||
|
||||
$(CHIPNAME).json: $(SRCS)
|
||||
@echo ">>> Synth"
|
||||
@echo
|
||||
$(YOSYS) -p "$(SYNTH_CMD)" > synth.log
|
||||
tail -n 35 synth.log
|
||||
|
||||
|
||||
$(CHIPNAME).asc: $(CHIPNAME).json $(CHIPNAME).pcf
|
||||
@echo ">>> Place and Route"
|
||||
@echo
|
||||
$(NEXTPNR) -r --$(DEVICE) --package $(PACKAGE) --pcf $(CHIPNAME).pcf --json $(CHIPNAME).json --asc $(CHIPNAME).asc $(PNR_OPT) --quiet --log pnr.log
|
||||
@grep "Info: Max frequency for clock " pnr.log | tail -n 1
|
||||
|
||||
$(CHIPNAME).bin: $(CHIPNAME).asc
|
||||
@echo ">>> Generate Bitstream"
|
||||
@echo
|
||||
icepack -s $(CHIPNAME).asc $(CHIPNAME).bin
|
||||
|
||||
clean::
|
||||
rm -f $(CHIPNAME).json $(CHIPNAME).asc $(CHIPNAME).bin $(CHIPNAME)_synth.v
|
||||
rm -f synth.log pnr.log
|
||||
|
||||
# Code for trying n different pnr seeds and reporting results
|
||||
|
||||
PNR_N_TRIES := 100
|
||||
PNR_TRY_LIST := $(shell seq $(PNR_N_TRIES))
|
||||
|
||||
pnr_sweep: $(addprefix pnr_try,$(PNR_TRY_LIST))
|
||||
|
||||
define make-sweep-target
|
||||
pnr_try$1: synth
|
||||
@echo ">>> Starting sweep $1"
|
||||
-$(NEXTPNR) --seed $1 --$(DEVICE) --package $(PACKAGE) --pcf $(CHIPNAME).pcf --json $(CHIPNAME).json --asc pnr_try$1.asc $(PNR_OPT) --quiet --log pnr$1.log
|
||||
@grep "Info: Max frequency for clock " pnr$1.log | tail -n 1
|
||||
endef
|
||||
|
||||
$(foreach try,$(PNR_TRY_LIST),$(eval $(call make-sweep-target,$(try))))
|
||||
|
||||
clean::
|
||||
rm -f pnr_try*.asc pnr*.log
|
||||
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Simple utility for programming SPI flash via a UART shell in the bootloader.
|
||||
# The bootloader possesses a 1-page buffer which we can write to, read from and
|
||||
# checksum.
|
||||
# It also possesses commands for transfers between this buffer and the SPI flash,
|
||||
# flash erasure, and launching the 2nd stage boot code.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
import sys
|
||||
import time
|
||||
|
||||
PAGESIZE = 2 ** 8
|
||||
SECTORSIZE = 2 ** 12
|
||||
BLOCKSIZE = 2 ** 16
|
||||
|
||||
SRAM_LOAD_ADDR = 0x2 << 28
|
||||
SRAM_EXEC_ADDR = SRAM_LOAD_ADDR + 0xc0
|
||||
|
||||
class ProtocolError(Exception):
|
||||
pass
|
||||
|
||||
class CMD:
|
||||
NOP = '\n'.encode()
|
||||
WRITE_BUF = 'w'.encode()
|
||||
READ_BUF = 'r'.encode()
|
||||
GET_CHECKSUM = 'c'.encode()
|
||||
SET_ADDR = 'a'.encode()
|
||||
WRITE_FLASH = 'W'.encode()
|
||||
READ_FLASH = 'R'.encode()
|
||||
ERASE_SECTOR = 'E'.encode()
|
||||
ERASE_BLOCK = 'B'.encode()
|
||||
BOOT_2ND = '2'.encode()
|
||||
LOAD_MEM = 'l'.encode()
|
||||
EXEC_MEM = 'x'.encode()
|
||||
ACK = ':'.encode()
|
||||
|
||||
def reset_shell(port):
|
||||
while True:
|
||||
port.write(CMD.NOP * (PAGESIZE + 1))
|
||||
port.flushOutput()
|
||||
time.sleep(0.02)
|
||||
port.flushInput()
|
||||
port.write(CMD.NOP)
|
||||
time.sleep(0.02)
|
||||
if port.readable() and port.read_all().endswith(CMD.ACK):
|
||||
break
|
||||
|
||||
def check_ack(port):
|
||||
resp = port.read()
|
||||
if len(resp) == 0 or resp != CMD.ACK:
|
||||
print("Got '{!r}'".format(resp))
|
||||
raise ProtocolError()
|
||||
|
||||
def write_buf(port, data, check=True):
|
||||
assert(len(data) == 256)
|
||||
port.write(CMD.WRITE_BUF)
|
||||
port.write(data)
|
||||
if check:
|
||||
check_ack(port)
|
||||
# TODO checksum
|
||||
|
||||
def read_buf(port, pipelined=False):
|
||||
if not pipelined:
|
||||
port.write(CMD.READ_BUF)
|
||||
resp = port.read(PAGESIZE)
|
||||
if len(resp) != PAGESIZE:
|
||||
raise ProtocolError()
|
||||
return resp
|
||||
# TODO checksum
|
||||
|
||||
def set_addr(port, addr):
|
||||
addr_b = bytes([
|
||||
(addr >> 16) & 0xff,
|
||||
(addr >> 8) & 0xff,
|
||||
(addr >> 0) & 0xff
|
||||
])
|
||||
port.write(CMD.SET_ADDR + addr_b)
|
||||
echo = port.read(3)
|
||||
if echo != addr_b:
|
||||
raise ProtocolError()
|
||||
check_ack(port)
|
||||
|
||||
def progress(header, frac, width=40):
|
||||
n = int(width * frac)
|
||||
sys.stdout.write("\r" + header + "▕" + "▒" * n + " " * (width - n) + "▏")
|
||||
|
||||
def read_flash(port, addr, size):
|
||||
set_addr(port, addr)
|
||||
data = bytes()
|
||||
addr_range = range(addr, addr + size, PAGESIZE)
|
||||
for a in addr_range:
|
||||
port.write(CMD.READ_FLASH)
|
||||
port.write(CMD.READ_BUF) # Should have space for 2 cmds in FIFO. Hoist this one up from read_buf()
|
||||
progress("Read: ", (a - addr) / size)
|
||||
check_ack(port)
|
||||
data += read_buf(port, pipelined=True)
|
||||
progress("Read: ", 1)
|
||||
if len(data) > size:
|
||||
data = data[:size] # ouch
|
||||
return data
|
||||
|
||||
def write_flash(port, addr, data):
|
||||
assert(addr % PAGESIZE == 0)
|
||||
if len(data) % PAGESIZE != 0:
|
||||
data += bytes(PAGESIZE - len(data) % PAGESIZE)
|
||||
set_addr(port, addr)
|
||||
for i in range(len(data) // PAGESIZE):
|
||||
write_buf(port, data[i * PAGESIZE : (i + 1) * PAGESIZE], check=False)
|
||||
port.write(CMD.WRITE_FLASH)
|
||||
progress("Write: ", i / (len(data) // PAGESIZE))
|
||||
check_ack(port)
|
||||
check_ack(port)
|
||||
progress("Write: ", 1)
|
||||
|
||||
def erase_flash(port, addr, size):
|
||||
end = addr + size
|
||||
if end % SECTORSIZE != 0:
|
||||
end += SECTORSIZE - end % SECTORSIZE
|
||||
start = addr - addr % SECTORSIZE
|
||||
set_addr(port, start)
|
||||
a = start
|
||||
while a < end:
|
||||
progress("Erase: ", (a - start) / (end - start))
|
||||
if end - a >= BLOCKSIZE and a % BLOCKSIZE == 0:
|
||||
port.write(CMD.ERASE_BLOCK)
|
||||
a += BLOCKSIZE
|
||||
else:
|
||||
port.write(CMD.ERASE_SECTOR)
|
||||
a += SECTORSIZE
|
||||
check_ack(port)
|
||||
progress("Erase: ", 1)
|
||||
|
||||
def run_2nd_stage(port):
|
||||
set_addr(port, 0x123456)
|
||||
port.write(CMD.BOOT_2ND)
|
||||
check_ack(port)
|
||||
|
||||
def load_mem(port, data):
|
||||
set_addr(port, 0xb00000 | SRAM_LOAD_ADDR)
|
||||
port.write(CMD.LOAD_MEM + bytes([
|
||||
(len(data) >> 16) & 0xff,
|
||||
(len(data) >> 8) & 0xff,
|
||||
(len(data) >> 0) & 0xff
|
||||
]))
|
||||
check_ack(port)
|
||||
port.write(data) # yup
|
||||
check_ack(port)
|
||||
|
||||
def exec_mem(port):
|
||||
set_addr(port, 0xb00000 | SRAM_EXEC_ADDR)
|
||||
port.write(CMD.EXEC_MEM)
|
||||
check_ack(port)
|
||||
|
||||
def any_int(x):
|
||||
return int(x, 0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("file", nargs="?", help="Filename to read from or dump to.")
|
||||
parser.add_argument("--uart", "-u", help="Path to UART device (e.g. /dev/ttyUSB0)")
|
||||
parser.add_argument("--baud", "-b", help="Baud rate for UART. Default 1 Mbaud")
|
||||
parser.add_argument("--write", "-w", action="store_true",
|
||||
help="Write to flash (default is read)")
|
||||
parser.add_argument("--verify", "-v", action="store_true",
|
||||
help="Verify contents after programming (use with --write)")
|
||||
parser.add_argument("--run", "-r", action="store_true",
|
||||
help="Run code from flash after programming")
|
||||
parser.add_argument("--execute", "-x", action="store_true",
|
||||
help="Load code directly into SRAM and execute it. Not to be combined with -w,-v,-r")
|
||||
parser.add_argument("--start", "-s", type=any_int, help="Base address to start read/write")
|
||||
parser.add_argument("--len", "-l", type=any_int, help="Number of bytes to read, or override file length for write.")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Do some simple parameter checking to make it harder to accidentally trash your device
|
||||
if args.write and args.file is None:
|
||||
sys.exit("Must specify filename for write")
|
||||
if args.start is None:
|
||||
args.start = 0
|
||||
if args.uart is None:
|
||||
args.uart = sorted(p.device for p in serial.tools.list_ports.comports())[-1]
|
||||
if args.baud is None:
|
||||
args.baud = 1000000
|
||||
if args.verify and not args.write:
|
||||
sys.exit("Verify is only valid for a write operation")
|
||||
if args.run and not args.write:
|
||||
sys.exit("Run is only valid for a write operation")
|
||||
if args.write and (args.start % PAGESIZE != 0):
|
||||
sys.exit("Writes must be aligned on a {}-byte boundary.".format(PAGESIZE))
|
||||
if args.execute and (args.write or args.verify or args.run or args.start or args.len):
|
||||
sys.exit("--execute is not compatible with flash-related arguments")
|
||||
if args.write or args.execute:
|
||||
try:
|
||||
filesize = os.stat(args.file).st_size
|
||||
if args.len is None:
|
||||
args.len = filesize
|
||||
except:
|
||||
sys.exit("Could not open file '{}'".format(args.file))
|
||||
else:
|
||||
if args.len is None:
|
||||
args.len = PAGESIZE
|
||||
|
||||
# Don't want to overwrite their image if they forget -w
|
||||
if not (args.write or args.execute) and args.file and os.path.exists(args.file):
|
||||
resp = ""
|
||||
while not resp in ["y", "n"]:
|
||||
resp = input("File '{}' exists. Overwrite? (y/n) ".format(args.file))
|
||||
resp = resp.lower().strip()
|
||||
if resp == "n":
|
||||
sys.exit(0)
|
||||
|
||||
# Summarise what we're about to do
|
||||
if args.execute:
|
||||
print("Loading {} bytes to SRAM and running".format(args.len))
|
||||
else:
|
||||
print("{} {} bytes {} {}, starting at address 0x{:06x}".format(
|
||||
"Writing" + (" and verifying" if args.verify else "") if args.write else "Reading",
|
||||
args.len,
|
||||
"from" if args.write else "to",
|
||||
"stdout" if args.file is None else "file " + args.file,
|
||||
args.start,
|
||||
", and verifying" if args.verify else ""
|
||||
))
|
||||
|
||||
# And then do it
|
||||
# need to allow for page erase, upward of 60 ms. (Uh, actually it seems to be much longer)
|
||||
port = serial.Serial(args.uart, args.baud, timeout=1)
|
||||
print("Waiting for bootloader on {}...".format(port.name))
|
||||
reset_shell(port)
|
||||
print("")
|
||||
|
||||
if args.execute:
|
||||
print("Loading...")
|
||||
load_mem(port, open(args.file, "rb").read())
|
||||
print("Load ok, running...")
|
||||
exec_mem(port)
|
||||
elif args.write:
|
||||
data = open(args.file, "rb").read()
|
||||
if len(data) > args.len:
|
||||
data = data[:args.len]
|
||||
else:
|
||||
data = data + bytes(args.len - len(data)) # zero padding
|
||||
|
||||
start = time.time()
|
||||
erase_flash(port, args.start, args.len)
|
||||
print(" Took {:.1f} s\n".format(time.time() - start))
|
||||
start = time.time()
|
||||
write_flash(port, args.start, data)
|
||||
print(" Took {:.1f} s\n".format(time.time() - start))
|
||||
if args.verify:
|
||||
start = time.time()
|
||||
readback = read_flash(port, args.start, args.len)
|
||||
print(" Took {:.1f} s".format(time.time() - start))
|
||||
if readback != data:
|
||||
sys.exit("Verification failed.") # TODO: better info
|
||||
if args.run:
|
||||
print("Launching flash second stage")
|
||||
run_2nd_stage(port)
|
||||
print("Done")
|
||||
else:
|
||||
start = time.time()
|
||||
data = read_flash(port, args.start, args.len)
|
||||
print(" Took {:.1f} s\nDone".format(time.time() - start))
|
||||
if args.file:
|
||||
open(args.file, "wb").write(data)
|
||||
else:
|
||||
for i, byte in enumerate(data):
|
||||
if i % 8 == 0:
|
||||
sys.stdout.write("{:06x}: ".format(args.start + i))
|
||||
sys.stdout.write("{:02x}".format(byte))
|
||||
sys.stdout.write("\n" if i % 8 == 7 else " ")
|
||||
sys.stdout.write("\n")
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
from PIL import Image
|
||||
from collections import OrderedDict
|
||||
|
||||
__doc__ = """Utility for stripping non-unique tiles from a tileset image.
|
||||
Outputs the uniquified image, and an index map file from original tile indices
|
||||
to new tile indices."""
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("input", help="Input file name")
|
||||
parser.add_argument("output", help="Output file name")
|
||||
parser.add_argument("--tilesize", "-t", help="Tile size (pixels), default 8",
|
||||
default="8", choices=["8", "16"])
|
||||
parser.epilog = __doc__
|
||||
args = parser.parse_args()
|
||||
|
||||
img = Image.open(args.input)
|
||||
tilesize = int(args.tilesize)
|
||||
|
||||
tile_first_seen = {}
|
||||
index_mapping = []
|
||||
output_images = []
|
||||
|
||||
src_index = 0
|
||||
for y in range(0, img.height - (tilesize - 1), tilesize):
|
||||
for x in range(0, img.width - (tilesize - 1), tilesize):
|
||||
tile = img.crop((x, y, x + tilesize, y + tilesize))
|
||||
tiledata = tuple(tile.getdata())
|
||||
if tiledata in tile_first_seen:
|
||||
index_mapping.append((src_index, tile_first_seen[tiledata]))
|
||||
else:
|
||||
tile_first_seen[tiledata] = len(output_images)
|
||||
index_mapping.append((src_index, len(output_images)))
|
||||
output_images.append(tile)
|
||||
src_index += 1
|
||||
|
||||
print("Found {} unique tile images".format(len(output_images)))
|
||||
|
||||
oimg = Image.new("RGBA", (tilesize * len(output_images), tilesize))
|
||||
for i, tile in enumerate(output_images):
|
||||
oimg.paste(tile, (i * tilesize, 0))
|
||||
|
||||
with open(args.output, "wb") as ofile:
|
||||
oimg.save(ofile)
|
||||
with open(args.output + ".index_map", "w") as mapfile:
|
||||
for old, new in index_mapping:
|
||||
mapfile.write("{}, {}\n".format(old, new))
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Tool for converting width of verilog hex files
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import re
|
||||
|
||||
# Assume little-endian, and that widths are multiples of 4
|
||||
|
||||
def width_convert(ilines, width, base):
|
||||
olines = []
|
||||
pad_fmt = "{:>0" + str(width // 4) + "}"
|
||||
accum = ""
|
||||
for l in ilines:
|
||||
if l.startswith("@"):
|
||||
if len(accum):
|
||||
olines.append(pad_fmt.format(accum))
|
||||
accum = ""
|
||||
# TODO: address scaling wrong if input not byte-sized:
|
||||
olines.append("@{:x}".format((int(l[1:], 16) - base) // (width // 8)))
|
||||
continue
|
||||
for num in re.findall(r"[0-9a-fA-F]+", l):
|
||||
accum = num + accum
|
||||
while len(accum) >= width // 4:
|
||||
olines.append(accum[len(accum) - width // 4 :])
|
||||
accum = accum[:len(accum) - width // 4]
|
||||
if len(accum):
|
||||
olines.append(pad_fmt.format(accum))
|
||||
return olines
|
||||
|
||||
def parseint(arg, name, default):
|
||||
if arg is None:
|
||||
arg = default
|
||||
try:
|
||||
if type(arg) is str and arg.startswith("0x"):
|
||||
arg = int(arg, 16)
|
||||
else:
|
||||
arg = int(arg)
|
||||
except ValueError:
|
||||
sys.exit("Invalid value for {}: {}".format(name, arg))
|
||||
return arg
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument("input", help="Input file name")
|
||||
parser.add_argument("--width", "-w", help="Output hex width (default 32)")
|
||||
parser.add_argument("--base", "-b", help="Base address for @ commands (subtracted)")
|
||||
parser.add_argument("--output", "-o", help="Output file name")
|
||||
args = parser.parse_args()
|
||||
if args.output is None:
|
||||
ofile = sys.stdout
|
||||
else:
|
||||
ofile = open(args.output, "w")
|
||||
if args.input is None:
|
||||
ifile = sys.stdin
|
||||
else:
|
||||
ifile = open(args.input)
|
||||
width = parseint(args.width, "width", 32)
|
||||
base = parseint(args.base, "base", 0)
|
||||
olines = width_convert(ifile, width, base)
|
||||
for l in olines:
|
||||
ofile.write(l + "\n")
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
# Link up to project root
|
||||
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../project_paths.mk
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#ifndef _HAZARD3_CSR_H
|
||||
#define _HAZARD3_CSR_H
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
#include "stdint.h"
|
||||
#endif
|
||||
|
||||
#define hazard3_csr_dmdata0 0xbff // Debug-mode shadow CSR for DM data transfer
|
||||
|
||||
#define hazard3_csr_meiea 0xbe0 // External interrupt pending array
|
||||
#define hazard3_csr_meipa 0xbe1 // External interrupt enable array
|
||||
#define hazard3_csr_meifa 0xbe2 // External interrupt force array
|
||||
#define hazard3_csr_meipra 0xbe3 // External interrupt priority array
|
||||
#define hazard3_csr_meinext 0xbe4 // Next external interrupt
|
||||
#define hazard3_csr_meicontext 0xbe5 // External interrupt context register
|
||||
|
||||
#define hazard3_csr_msleep 0xbf0 // M-mode sleep control register
|
||||
|
||||
#define hazard3_csr_pmpcfgm0 0xbd0 // Non-locking M-mode enables for PMP regions
|
||||
|
||||
#define _read_csr(csrname) ({ \
|
||||
uint32_t __csr_tmp_u32; \
|
||||
asm volatile ("csrr %0, " #csrname : "=r" (__csr_tmp_u32)); \
|
||||
__csr_tmp_u32; \
|
||||
})
|
||||
|
||||
#define _write_csr(csrname, data) ({ \
|
||||
asm volatile ("csrw " #csrname ", %0" : : "r" (data)); \
|
||||
})
|
||||
|
||||
#define _set_csr(csrname, data) ({ \
|
||||
asm volatile ("csrs " #csrname ", %0" : : "r" (data)); \
|
||||
})
|
||||
|
||||
#define _clear_csr(csrname, data) ({ \
|
||||
asm volatile ("csrc " #csrname ", %0" : : "r" (data)); \
|
||||
})
|
||||
|
||||
#define _read_write_csr(csrname, data) ({ \
|
||||
uint32_t __csr_tmp_u32; \
|
||||
asm volatile ("csrrw %0, " #csrname ", %1" : "=r" (__csr_tmp_u32) : "r" (data)); \
|
||||
__csr_tmp_u32; \
|
||||
})
|
||||
|
||||
#define _read_set_csr(csrname, data) ({ \
|
||||
uint32_t __csr_tmp_u32; \
|
||||
asm volatile ("csrrs %0, " #csrname ", %1" : "=r" (__csr_tmp_u32) : "r" (data)); \
|
||||
__csr_tmp_u32; \
|
||||
})
|
||||
|
||||
#define _read_clear_csr(csrname, data) ({ \
|
||||
uint32_t __csr_tmp_u32; \
|
||||
asm volatile ("csrrc %0, " #csrname ", %1" : "=r" (__csr_tmp_u32) : "r" (data)); \
|
||||
__csr_tmp_u32; \
|
||||
})
|
||||
|
||||
// Argument macro expansion layer
|
||||
#define read_csr(csrname) _read_csr(csrname)
|
||||
#define write_csr(csrname, data) _write_csr(csrname, data)
|
||||
#define set_csr(csrname, data) _set_csr(csrname, data)
|
||||
#define clear_csr(csrname, data) _clear_csr(csrname, data)
|
||||
#define read_write_csr(csrname, data) _read_write_csr(csrname, data)
|
||||
#define read_set_csr(csrname, data) _read_set_csr(csrname, data)
|
||||
#define read_clear_csr(csrname, data) _read_clear_csr(csrname, data)
|
||||
|
||||
#endif
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#ifndef _HAZARD3_INSTR_H
|
||||
#define _HAZARD3_INSTR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// C macros for Hazard3 custom instructions
|
||||
|
||||
// nbits must be a constant expression
|
||||
#define __hazard3_bextm(nbits, rs1, rs2) ({\
|
||||
uint32_t __h3_bextm_rd; \
|
||||
asm (".insn r 0x0b, 0, %3, %0, %1, %2"\
|
||||
: "=r" (__h3_bextm_rd) \
|
||||
: "r" (rs1), "r" (rs2), "i" ((((nbits) - 1) & 0x7) << 1)\
|
||||
); \
|
||||
__h3_bextm_rd; \
|
||||
})
|
||||
|
||||
// nbits and shamt must be constant expressions
|
||||
#define __hazard3_bextmi(nbits, rs1, shamt) ({\
|
||||
uint32_t __h3_bextmi_rd; \
|
||||
asm (".insn i 0x0b, 0x4, %0, %1, %2"\
|
||||
: "=r" (__h3_bextmi_rd) \
|
||||
: "r" (rs1), "i" ((((nbits) - 1) & 0x7) << 6 | ((shamt) & 0x1f)) \
|
||||
); \
|
||||
__h3_bextmi_rd; \
|
||||
})
|
||||
|
||||
#define __hazard3_block() asm ("slt x0, x0, x0" : : : "memory")
|
||||
|
||||
#define __hazard3_unblock() asm ("slt x0, x0, x1" : : : "memory")
|
||||
|
||||
#endif
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
#ifndef _HAZARD3_IRQ_H
|
||||
#define _HAZARD3_IRQ_H
|
||||
|
||||
#include "hazard3_csr.h"
|
||||
#include "stdint.h"
|
||||
#include "stdbool.h"
|
||||
|
||||
// Should match processor configuration in testbench:
|
||||
#define NUM_IRQS 32
|
||||
#define MAX_PRIORITY 15
|
||||
|
||||
// Declarations for irq_dispatch.S
|
||||
extern uintptr_t _external_irq_table[NUM_IRQS];
|
||||
extern uint32_t _external_irq_entry_count;
|
||||
|
||||
#define h3irq_array_read(csr, index) (read_set_csr(csr, (index)) >> 16)
|
||||
|
||||
#define h3irq_array_write(csr, index, data) (write_csr(csr, (index) | ((uint32_t)(data) << 16)))
|
||||
#define h3irq_array_set(csr, index, data) (set_csr(csr, (index) | ((uint32_t)(data) << 16)))
|
||||
#define h3irq_array_clear(csr, index, data) (clear_csr(csr, (index) | ((uint32_t)(data) << 16)))
|
||||
|
||||
static inline void h3irq_enable(unsigned int irq, bool enable) {
|
||||
if (enable) {
|
||||
h3irq_array_set(hazard3_csr_meiea, irq >> 4, 1u << (irq & 0xfu));
|
||||
}
|
||||
else {
|
||||
h3irq_array_clear(hazard3_csr_meiea, irq >> 4, 1u << (irq & 0xfu));
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool h3irq_pending(unsigned int irq) {
|
||||
return h3irq_array_read(hazard3_csr_meipa, irq >> 4) & (1u << (irq & 0xfu));
|
||||
}
|
||||
|
||||
static inline void h3irq_force_pending(unsigned int irq, bool force) {
|
||||
if (force) {
|
||||
h3irq_array_set(hazard3_csr_meifa, irq >> 4, 1u << (irq & 0xfu));
|
||||
}
|
||||
else {
|
||||
h3irq_array_clear(hazard3_csr_meifa, irq >> 4, 1u << (irq & 0xfu));
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool h3irq_is_forced(unsigned int irq) {
|
||||
return h3irq_array_read(hazard3_csr_meifa, irq >> 4) & (1u << (irq & 0xfu));
|
||||
}
|
||||
|
||||
// -1 for no IRQ
|
||||
static inline int h3irq_get_current_irq() {
|
||||
uint32_t meicontext = read_csr(hazard3_csr_meicontext);
|
||||
return meicontext & 0x8000u ? -1 : (meicontext >> 4) & 0x1ffu;
|
||||
}
|
||||
|
||||
static inline void h3irq_set_priority(unsigned int irq, uint32_t priority) {
|
||||
// Don't want read-modify-write, but no instruction for atomically writing
|
||||
// a bitfield. So, first drop priority to minimum, then set to the target
|
||||
// value. It should be safe to drop an IRQ's priority below its current
|
||||
// even from within that IRQ (but it is never safe to boost an IRQ when
|
||||
// it may already be in an older stack frame)
|
||||
h3irq_array_clear(hazard3_csr_meipra, irq >> 2, 0xfu << (4 * (irq & 0x3)));
|
||||
h3irq_array_set(hazard3_csr_meipra, irq >> 2, (priority & 0xfu) << (4 * (irq & 0x3)));
|
||||
}
|
||||
|
||||
static inline void h3irq_set_handler(unsigned int irq, void (*handler)(void)) {
|
||||
_external_irq_table[irq] = (uintptr_t)handler;
|
||||
}
|
||||
|
||||
static inline void global_irq_enable(bool en) {
|
||||
// mstatus.mie
|
||||
if (en) {
|
||||
set_csr(mstatus, 0x8);
|
||||
}
|
||||
else {
|
||||
clear_csr(mstatus, 0x8);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void external_irq_enable(bool en) {
|
||||
// mie.meie
|
||||
if (en) {
|
||||
set_csr(mie, 0x800);
|
||||
}
|
||||
else {
|
||||
clear_csr(mie, 0x800);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void timer_irq_enable(bool en) {
|
||||
// mie.mtie
|
||||
if (en) {
|
||||
set_csr(mie, 0x080);
|
||||
}
|
||||
else {
|
||||
clear_csr(mie, 0x080);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
#include "hazard3_csr.h"
|
||||
|
||||
#define IO_BASE 0xc0000000
|
||||
#define IO_PRINT_CHAR (IO_BASE + 0x0)
|
||||
#define IO_PRINT_U32 (IO_BASE + 0x4)
|
||||
#define IO_EXIT (IO_BASE + 0x8)
|
||||
|
||||
// Provide trap vector table, reset handler and weak default trap handlers for
|
||||
// Hazard3. This is not a crt0: the reset handler calls an external _start
|
||||
|
||||
.option push
|
||||
.option norelax
|
||||
.file 1 "vendor/Hazard3/test/sim/common/init.S"
|
||||
.section .vectors,"ax",@progbits
|
||||
|
||||
.macro VEC name:req
|
||||
.p2align 2
|
||||
j \name
|
||||
.p2align 2
|
||||
.endm
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Vector table (must be at least aligned to its size rounded up to power of 2)
|
||||
|
||||
.p2align 12
|
||||
.vector_table:
|
||||
|
||||
// Single exception vector, also takes IRQs if vectoring is disabled
|
||||
|
||||
VEC handle_exception
|
||||
|
||||
// Standard interrupts, if vectoring is enabled
|
||||
// Note: global EIRQ does not fire. Instead we have 16 separate vectors
|
||||
|
||||
// handle_exception ^^^ takes the slot where U-mode softirq would be
|
||||
VEC .halt
|
||||
VEC .halt
|
||||
VEC isr_machine_softirq
|
||||
VEC .halt
|
||||
VEC .halt
|
||||
VEC .halt
|
||||
VEC isr_machine_timer
|
||||
VEC .halt
|
||||
VEC .halt
|
||||
VEC .halt
|
||||
VEC isr_external_irq
|
||||
VEC .halt
|
||||
VEC .halt
|
||||
VEC .halt
|
||||
VEC .halt
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Reset handler
|
||||
|
||||
|
||||
.reset_handler:
|
||||
// Set counters running, as they are off by default. This may trap if counters
|
||||
// are unimplemented, so catch the trap and continue.
|
||||
.loc 1 59 0 ; la a0, 1f
|
||||
.loc 1 60 0 ; csrw mtvec, a0
|
||||
.loc 1 61 0 ; csrci mcountinhibit, 0x5
|
||||
.loc 1 62 0 ; j 2f
|
||||
.p2align 2
|
||||
1:
|
||||
.loc 1 65 0 ; csrw mcause, zero
|
||||
2:
|
||||
|
||||
// Set up trap vector table. mtvec LSB enables vectoring
|
||||
.loc 1 69 0 ; la a0, .vector_table + 1
|
||||
.loc 1 70 0 ; csrw mtvec, a0
|
||||
|
||||
// Ensure gp is initialised on all cores -- don't wait for newlib _start
|
||||
// as that is core-0-only
|
||||
.option push
|
||||
.option norelax
|
||||
.loc 1 76 0 ; la gp, __global_pointer$
|
||||
.option pop
|
||||
|
||||
// Put spare cores to sleep before setting up core 0 stack
|
||||
// Note csrr is a NOP when there are no CSRs at all (no traps!):
|
||||
.loc 1 81 0 ; li a0, 0
|
||||
.loc 1 82 0 ; csrr a0, mhartid
|
||||
.loc 1 83 0 ; bnez a0, .core1_wait
|
||||
|
||||
// Set up stack pointer before doing anything else
|
||||
.loc 1 86 0 ; la sp, __stack_top
|
||||
|
||||
// newlib _start expects argc, argv on the stack. Leave stack 16-byte aligned.
|
||||
.loc 1 89 0 ; addi sp, sp, -16
|
||||
.loc 1 90 0 ; li a0, 1
|
||||
.loc 1 91 0 ; sw a0, (sp)
|
||||
.loc 1 92 0 ; la a0, progname
|
||||
.loc 1 93 0 ; sw a0, 4(sp)
|
||||
|
||||
.loc 1 95 0 ; jal _start
|
||||
.loc 1 96 0 ; j .halt
|
||||
|
||||
.core1_wait:
|
||||
// IRQs disabled, but soft IRQ unmasked -> soft IRQ will exit WFI.
|
||||
csrci mstatus, 0x8
|
||||
csrw mie, 0x8
|
||||
.core1_wait_loop:
|
||||
wfi
|
||||
la a0, core1_entry_vector
|
||||
lw a0, (a0)
|
||||
beqz a0, .core1_wait_loop
|
||||
la sp, __stack_top - 0x10000
|
||||
jalr a0
|
||||
.core1_finish:
|
||||
wfi
|
||||
j .core1_finish
|
||||
|
||||
.p2align 2
|
||||
.global core1_entry_vector
|
||||
core1_entry_vector:
|
||||
.word 0
|
||||
|
||||
.global _exit
|
||||
_exit:
|
||||
li a1, IO_EXIT
|
||||
sw a0, (a1)
|
||||
|
||||
.global _sbrk
|
||||
_sbrk:
|
||||
la a1, heap_ptr
|
||||
lw a2, (a1)
|
||||
add a0, a0, a2
|
||||
sw a0, (a1)
|
||||
mv a0, a2
|
||||
ret
|
||||
|
||||
.p2align 2
|
||||
heap_ptr:
|
||||
.word _end
|
||||
|
||||
.global .halt
|
||||
.halt:
|
||||
j .halt
|
||||
|
||||
progname:
|
||||
.asciz "hazard3-testbench"
|
||||
|
||||
.p2align 2
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Weak handler/ISR symbols
|
||||
|
||||
// Routine to print out trap name, trap address, and some core registers
|
||||
// (x8..x15, ra, sp). The default handlers are all patched into this routine,
|
||||
// so the CPU will print some basic diagnostics on any unhandled trap
|
||||
// (assuming the processor is not internally completely broken)
|
||||
|
||||
// argument in t0, IO pointer in t1, return in tp, trashes t0 and t2;
|
||||
_tb_puts:
|
||||
1:
|
||||
lbu t2, (t0)
|
||||
addi t0, t0, 1
|
||||
beqz t2, 2f
|
||||
sw t2, IO_PRINT_CHAR - IO_BASE(t1)
|
||||
j 1b
|
||||
2:
|
||||
jr tp
|
||||
|
||||
.macro print_reg str reg
|
||||
la t0, \str
|
||||
jal tp, _tb_puts
|
||||
sw \reg, IO_PRINT_U32 - IO_BASE(t1)
|
||||
.endm
|
||||
|
||||
_weak_handler_name_in_gp:
|
||||
la t0, _str_unhandled_trap
|
||||
li t1, IO_BASE
|
||||
jal tp, _tb_puts
|
||||
mv t0, gp
|
||||
jal tp, _tb_puts
|
||||
la t0, _str_at_mepc
|
||||
jal tp, _tb_puts
|
||||
csrr t0, mepc
|
||||
sw t0, IO_PRINT_U32 - IO_BASE(t1)
|
||||
csrr gp, mcause
|
||||
bltz gp, 1f
|
||||
print_reg _str_mcause gp
|
||||
1:
|
||||
print_reg _str_s0 s0
|
||||
print_reg _str_s1 s1
|
||||
print_reg _str_a0 a0
|
||||
print_reg _str_a1 a1
|
||||
print_reg _str_a2 a2
|
||||
print_reg _str_a3 a3
|
||||
print_reg _str_a4 a4
|
||||
print_reg _str_a5 a5
|
||||
print_reg _str_ra ra
|
||||
print_reg _str_sp sp
|
||||
li t2, -1
|
||||
sw t2, IO_EXIT - IO_BASE(t1)
|
||||
// Should be unreachable:
|
||||
j .halt
|
||||
|
||||
_str_unhandled_trap: .asciz "*** Unhandled trap ***\n"
|
||||
_str_at_mepc: .asciz " @ mepc = "
|
||||
_str_mcause: .asciz " mcause = "
|
||||
_str_s0: .asciz "s0: "
|
||||
_str_s1: .asciz "s1: "
|
||||
_str_a0: .asciz "a0: "
|
||||
_str_a1: .asciz "a1: "
|
||||
_str_a2: .asciz "a2: "
|
||||
_str_a3: .asciz "a3: "
|
||||
_str_a4: .asciz "a4: "
|
||||
_str_a5: .asciz "a5: "
|
||||
_str_ra: .asciz "ra: "
|
||||
_str_sp: .asciz "sp: "
|
||||
.p2align 2
|
||||
|
||||
// Provide a default weak handler for each trap, which calls into the above
|
||||
// diagnostic routine with the trap name (a null-terminated string) in gp
|
||||
|
||||
.macro weak_handler name:req
|
||||
.p2align 2
|
||||
.global \name
|
||||
.weak \name
|
||||
\name:
|
||||
la gp, _str_\name
|
||||
j _weak_handler_name_in_gp
|
||||
_str_\name:
|
||||
.asciz "\name"
|
||||
.endm
|
||||
|
||||
weak_handler handle_exception
|
||||
weak_handler isr_machine_softirq
|
||||
weak_handler isr_machine_timer
|
||||
weak_handler isr_external_irq
|
||||
|
||||
// You can relax now
|
||||
.option pop
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user