1 /***************************************************************************//**
2 * \file cyhal_hwmgr.c
3 *
4 * \brief
5 * Provides a high level interface for managing hardware resources. This is
6 * used to track what hardware blocks are already being used so they are not over
7 * allocated.
8 *
9 ********************************************************************************
10 * \copyright
11 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
12 * an affiliate of Cypress Semiconductor Corporation
13 *
14 * SPDX-License-Identifier: Apache-2.0
15 *
16 * Licensed under the Apache License, Version 2.0 (the "License");
17 * you may not use this file except in compliance with the License.
18 * You may obtain a copy of the License at
19 *
20 *     http://www.apache.org/licenses/LICENSE-2.0
21 *
22 * Unless required by applicable law or agreed to in writing, software
23 * distributed under the License is distributed on an "AS IS" BASIS,
24 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25 * See the License for the specific language governing permissions and
26 * limitations under the License.
27 *******************************************************************************/
28 
29 #include "cy_utils.h"
30 #include "cyhal_hwmgr.h"
31 #include "cyhal_system.h"
32 
33 #if defined(__cplusplus)
34 extern "C"
35 {
36 #endif
37 
38 #define CY_BYTE_NUM_SHIFT (3)
39 #define CY_BIT_NUM_MASK (0x07)
40 
41 // Get device specific resources and offsets
42 // This must declare:
43 //    _CYHAL_RESOURCES
44 //    typedef _cyhal_hwmgr_offset_t
45 //    static uint8_t cyhal_used[<appropriate size>] = {0};
46 //    static inline uint16_t _cyhal_uses_channels(cyhal_resource_t type);
47 //    static inline uint16_t _cyhal_get_resource_offset(cyhal_resource_t type);
48 //    static inline const _cyhal_hwmgr_offset_t* _cyhal_get_block_offsets(cyhal_resource_t type);
49 //    static inline uint8_t _cyhal_get_block_offset_length(cyhal_resource_t type);
50 #include "cyhal_hwmgr_impl_part.h"
51 
52 /*
53  * This function is designed to verify that the number of valid resources in the cyhal_resource_t
54  * enum and the number entries in the _CYHAL_RESOURCES array are identical. Any mismatch
55  * between the two will lead to runtime failures. This will produce a divide by 0 error if they
56  * get of of sync.
57  * NOTE: This function should never be called, it is only for a compile time error check
58  * NOTE: The suppress is to temporarily disable the IAR warning about an uncalled function
59  */
60 static inline void _check_array_size(void) __attribute__ ((deprecated));
61 #if __ICCARM__
62 #pragma diag_suppress=Pe177
63 #elif __clang__
64 #pragma clang diagnostic push
65 #pragma clang diagnostic ignored "-Wunused-function"
66 #endif
_check_array_size(void)67 static inline void _check_array_size(void)
68 {
69     uint32_t dummy = 1 / (_CYHAL_RESOURCES == CYHAL_RSC_INVALID);
70     (void)dummy;
71 }
72 #if __ICCARM__
73 #pragma diag_default=Pe177
74 #elif __clang__
75 #pragma clang diagnostic pop
76 #endif
77 
78 /*******************************************************************************
79 *       Utility helper functions
80 *******************************************************************************/
81 
_cyhal_get_bit_position(cyhal_resource_t type,uint8_t block,uint8_t channel,uint16_t * bitPosition)82 static cy_rslt_t _cyhal_get_bit_position(cyhal_resource_t type, uint8_t block, uint8_t channel, uint16_t* bitPosition)
83 {
84 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
85     /* For backwards compatability. */
86     if (type == CYHAL_RSC_CLKPATH)
87     {
88         channel = block;
89         block = CYHAL_CLOCK_BLOCK_PATHMUX;
90         type = CYHAL_RSC_CLOCK;
91     }
92 #endif
93     uint16_t offsetRsc = _cyhal_get_resource_offset(type);
94     // Offset that is one past the beginning of the next resource (or one past the end of the array).
95     // Our offset must be strictly less than that
96     uint16_t offsetEndOfRsc = ((1u + type) < _CYHAL_RESOURCES)
97         ? _cyhal_get_resource_offset((cyhal_resource_t)(type + 1))
98         : CY_TOTAL_ALLOCATABLE_ITEMS;
99 
100     if (_cyhal_uses_channels(type))
101     {
102         const _cyhal_hwmgr_offset_t* blockOffsets = _cyhal_get_block_offsets(type);
103         *bitPosition = offsetEndOfRsc;
104         if (blockOffsets != NULL)
105         {
106             // Offset (from the beginning of the section for this block type) that is one past the end of
107             // the requested block index. The channel number must be strictly less than that.
108             uint16_t blocks = _cyhal_get_block_offset_length(type);
109             if (block < blocks)
110             {
111                 *bitPosition = offsetRsc + blockOffsets[block] + channel;
112                 if ((block + 1) < blocks)
113                 {
114                     offsetEndOfRsc = offsetRsc + blockOffsets[block + 1];
115                 }
116             }
117         }
118     }
119     else
120     {
121         *bitPosition = offsetRsc + block;
122     }
123 
124     return (*bitPosition < offsetEndOfRsc)
125         ? CY_RSLT_SUCCESS
126         : CYHAL_HWMGR_RSLT_ERR_INVALID;
127 }
128 
_cyhal_is_set(const uint8_t * used,cyhal_resource_t type,uint8_t block,uint8_t channel,bool * isSet)129 static inline cy_rslt_t _cyhal_is_set(const uint8_t* used, cyhal_resource_t type, uint8_t block, uint8_t channel, bool* isSet)
130 {
131     uint16_t bitPosition;
132     cy_rslt_t status = _cyhal_get_bit_position(type, block, channel, &bitPosition);
133     if (status == CY_RSLT_SUCCESS)
134     {
135         uint8_t byte = (uint8_t)(bitPosition >> CY_BYTE_NUM_SHIFT);
136         uint8_t bit = bitPosition & CY_BIT_NUM_MASK;
137         *isSet = (used[byte] & (1 << bit));
138     }
139     return status;
140 }
141 
_cyhal_set_bit(uint8_t * used,cyhal_resource_t type,uint8_t block,uint8_t channel)142 static inline cy_rslt_t _cyhal_set_bit(uint8_t* used, cyhal_resource_t type, uint8_t block, uint8_t channel)
143 {
144     uint16_t bitPosition;
145     cy_rslt_t status = _cyhal_get_bit_position(type, block, channel, &bitPosition);
146     if (status == CY_RSLT_SUCCESS)
147     {
148         uint8_t byte = (uint8_t)(bitPosition >> CY_BYTE_NUM_SHIFT);
149         uint8_t bit = bitPosition & CY_BIT_NUM_MASK;
150         used[byte] |= (1 << bit);
151     }
152     return status;
153 }
154 
_cyhal_clear_bit(uint8_t * used,cyhal_resource_t type,uint8_t block,uint8_t channel)155 static inline cy_rslt_t _cyhal_clear_bit(uint8_t* used, cyhal_resource_t type, uint8_t block, uint8_t channel)
156 {
157     uint16_t bitPosition;
158     cy_rslt_t status = _cyhal_get_bit_position(type, block, channel, &bitPosition);
159     if (status == CY_RSLT_SUCCESS)
160     {
161         uint8_t byte = (uint8_t)(bitPosition >> CY_BYTE_NUM_SHIFT);
162         uint8_t bit = bitPosition & CY_BIT_NUM_MASK;
163         used[byte] &= ~(1 << bit);
164     }
165     return status;
166 }
167 
168 /*******************************************************************************
169 *       Hardware Manager API
170 *******************************************************************************/
171 
cyhal_hwmgr_init(void)172 cy_rslt_t cyhal_hwmgr_init(void)
173 {
174     return CY_RSLT_SUCCESS;
175 }
176 
cyhal_hwmgr_reserve(const cyhal_resource_inst_t * obj)177 cy_rslt_t cyhal_hwmgr_reserve(const cyhal_resource_inst_t* obj)
178 {
179     bool isSet;
180     uint32_t state = cyhal_system_critical_section_enter();
181     cy_rslt_t rslt = _cyhal_is_set(cyhal_used, obj->type, obj->block_num, obj->channel_num, &isSet);
182     if (rslt == CY_RSLT_SUCCESS && isSet)
183     {
184         rslt = CYHAL_HWMGR_RSLT_ERR_INUSE;
185     }
186 
187     if (rslt == CY_RSLT_SUCCESS)
188     {
189         rslt = _cyhal_set_bit(cyhal_used, obj->type, obj->block_num, obj->channel_num);
190     }
191     cyhal_system_critical_section_exit(state);
192 
193     return rslt;
194 }
195 
cyhal_hwmgr_free(const cyhal_resource_inst_t * obj)196 void cyhal_hwmgr_free(const cyhal_resource_inst_t* obj)
197 {
198     uint32_t state = cyhal_system_critical_section_enter();
199     cy_rslt_t rslt = _cyhal_clear_bit(cyhal_used, obj->type, obj->block_num, obj->channel_num);
200     CY_UNUSED_PARAMETER(rslt); /* CY_ASSERT only processes in DEBUG, ignores for others */
201     CY_ASSERT(CY_RSLT_SUCCESS == rslt);
202     cyhal_system_critical_section_exit(state);
203 }
204 
205 #if (CYHAL_DRIVER_AVAILABLE_INTERCONNECT)
cyhal_hwmgr_allocate(cyhal_resource_t type,cyhal_resource_inst_t * obj)206 cy_rslt_t cyhal_hwmgr_allocate(cyhal_resource_t type, cyhal_resource_inst_t* obj)
207 {
208     return _cyhal_hwmgr_allocate_with_connection(type, NULL, NULL, NULL, NULL, obj);
209 }
210 
_cyhal_hwmgr_allocate_with_connection(cyhal_resource_t type,const cyhal_source_t * src,const cyhal_dest_t * dest,_cyhal_hwmgr_get_output_source_t get_src,_cyhal_hwmgr_get_input_dest_t get_dest,cyhal_resource_inst_t * obj)211 cy_rslt_t _cyhal_hwmgr_allocate_with_connection(cyhal_resource_t type, const cyhal_source_t *src, const cyhal_dest_t *dest,
212     _cyhal_hwmgr_get_output_source_t get_src, _cyhal_hwmgr_get_input_dest_t get_dest, cyhal_resource_inst_t *obj)
213 #else
214 cy_rslt_t cyhal_hwmgr_allocate(cyhal_resource_t type, cyhal_resource_inst_t* obj)
215 #endif
216 {
217     uint16_t offsetStartOfRsc = _cyhal_get_resource_offset(type);
218     uint16_t offsetEndOfRsc = ((1u + type) < _CYHAL_RESOURCES)
219         ? _cyhal_get_resource_offset((cyhal_resource_t)(type + 1))
220         : CY_TOTAL_ALLOCATABLE_ITEMS;
221     bool usesChannels = _cyhal_uses_channels(type);
222 
223     uint16_t count = offsetEndOfRsc - offsetStartOfRsc;
224     uint8_t block = 0;
225     uint8_t channel = 0;
226     for (uint16_t i = 0; i < count; i++)
227     {
228 #if (CYHAL_DRIVER_AVAILABLE_INTERCONNECT)
229         bool valid = true;
230         if (NULL != src)
231         {
232             cyhal_dest_t destination = get_dest(block, channel);
233             valid = _cyhal_can_connect_signal(*src, destination);
234         }
235         if (valid && NULL != dest)
236         {
237             cyhal_source_t source = get_src(block, channel);
238             valid = _cyhal_can_connect_signal(source, *dest);
239         }
240         if (valid)
241 #endif
242         {
243             cyhal_resource_inst_t rsc = { type, block, channel };
244             if (CY_RSLT_SUCCESS == cyhal_hwmgr_reserve(&rsc))
245             {
246                 obj->type = type;
247                 obj->block_num = block;
248                 obj->channel_num = channel;
249                 return CY_RSLT_SUCCESS;
250             }
251         }
252 
253         if (usesChannels)
254         {
255             const _cyhal_hwmgr_offset_t* blockOffsets = _cyhal_get_block_offsets(type);
256             uint16_t blocks = _cyhal_get_block_offset_length(type);
257             if ((block + 1) < blocks && blockOffsets[block + 1] <= (i + 1))
258             {
259                 channel = 0;
260                 do
261                 {
262                     block++;
263                 }
264                 while (((block + 1) < blocks) && (blockOffsets[block + 1] == blockOffsets[block])); /* Skip empty blocks */
265             }
266             else
267             {
268                 channel++;
269             }
270         }
271         else
272         {
273             block++;
274         }
275     }
276 
277     return CYHAL_HWMGR_RSLT_ERR_NONE_FREE;
278 }
279 
280 #if defined(__cplusplus)
281 }
282 #endif
283