1 // Copyright 2017 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 #include "freertos/FreeRTOS.h"
16 #include "freertos/task.h"
17 #include "esp_app_trace_util.h"
18 #include "sdkconfig.h"
19 #if CONFIG_IDF_TARGET_ESP32
20 #include "esp32/clk.h"
21 #elif CONFIG_IDF_TARGET_ESP32S2
22 #include "esp32s2/clk.h"
23 #elif CONFIG_IDF_TARGET_ESP32S3
24 #include "esp32s3/clk.h"
25 #elif CONFIG_IDF_TARGET_ESP32C3
26 #include "esp32c3/clk.h"
27 #endif
28 
29 ///////////////////////////////////////////////////////////////////////////////
30 ///////////////////////////////// TIMEOUT /////////////////////////////////////
31 ///////////////////////////////////////////////////////////////////////////////
32 
33 #define ESP_APPTRACE_CPUTICKS2US(_t_, _cpu_freq_)       ((_t_)/(_cpu_freq_/1000000))
34 #define ESP_APPTRACE_US2CPUTICKS(_t_, _cpu_freq_)       ((_t_)*(_cpu_freq_/1000000))
35 
esp_apptrace_tmo_check(esp_apptrace_tmo_t * tmo)36 esp_err_t esp_apptrace_tmo_check(esp_apptrace_tmo_t *tmo)
37 {
38     int cpu_freq = esp_clk_cpu_freq();
39     if (tmo->tmo != ESP_APPTRACE_TMO_INFINITE) {
40         unsigned cur = portGET_RUN_TIME_COUNTER_VALUE();
41         if (tmo->start <= cur) {
42             tmo->elapsed = ESP_APPTRACE_CPUTICKS2US(cur - tmo->start, cpu_freq);
43         } else {
44             tmo->elapsed = ESP_APPTRACE_CPUTICKS2US(0xFFFFFFFF - tmo->start + cur, cpu_freq);
45         }
46         if (tmo->elapsed >= tmo->tmo) {
47             return ESP_ERR_TIMEOUT;
48         }
49     }
50     return ESP_OK;
51 }
52 
53 ///////////////////////////////////////////////////////////////////////////////
54 ///////////////////////////////// LOCK ////////////////////////////////////////
55 ///////////////////////////////////////////////////////////////////////////////
56 
esp_apptrace_lock_take(esp_apptrace_lock_t * lock,esp_apptrace_tmo_t * tmo)57 esp_err_t esp_apptrace_lock_take(esp_apptrace_lock_t *lock, esp_apptrace_tmo_t *tmo)
58 {
59     int res;
60 
61     while (1) {
62         // do not overwrite lock->int_state before we actually acquired the mux
63         unsigned int_state = portENTER_CRITICAL_NESTED();
64         // FIXME: if mux is busy it is not good idea to loop during the whole tmo with disabled IRQs.
65         // So we check mux state using zero tmo, restore IRQs and let others tasks/IRQs to run on this CPU
66         // while we are doing our own tmo check.
67 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
68         bool success = vPortCPUAcquireMutexTimeout(&lock->mux, 0, __FUNCTION__, __LINE__);
69 #else
70         bool success = vPortCPUAcquireMutexTimeout(&lock->mux, 0);
71 #endif
72         if (success) {
73             lock->int_state = int_state;
74             return ESP_OK;
75         }
76         portEXIT_CRITICAL_NESTED(int_state);
77         // we can be preempted from this place till the next call (above) to portENTER_CRITICAL_NESTED()
78         res = esp_apptrace_tmo_check(tmo);
79         if (res != ESP_OK) {
80             break;
81         }
82     }
83     return res;
84 }
85 
esp_apptrace_lock_give(esp_apptrace_lock_t * lock)86 esp_err_t esp_apptrace_lock_give(esp_apptrace_lock_t *lock)
87 {
88     // save lock's irq state value for this CPU
89     unsigned int_state = lock->int_state;
90     // after call to the following func we can not be sure that lock->int_state
91     // is not overwritten by other CPU who has acquired the mux just after we released it. See esp_apptrace_lock_take().
92 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
93     vPortCPUReleaseMutex(&lock->mux, __FUNCTION__, __LINE__);
94 #else
95     vPortCPUReleaseMutex(&lock->mux);
96 #endif
97     portEXIT_CRITICAL_NESTED(int_state);
98     return ESP_OK;
99 }
100 
101 ///////////////////////////////////////////////////////////////////////////////
102 ////////////////////////////// RING BUFFER ////////////////////////////////////
103 ///////////////////////////////////////////////////////////////////////////////
104 
esp_apptrace_rb_produce(esp_apptrace_rb_t * rb,uint32_t size)105 uint8_t *esp_apptrace_rb_produce(esp_apptrace_rb_t *rb, uint32_t size)
106 {
107     uint8_t *ptr = rb->data + rb->wr;
108     // check for avalable space
109     if (rb->rd <= rb->wr) {
110         // |?R......W??|
111         if (rb->wr + size >= rb->size) {
112             if (rb->rd == 0) {
113                 return NULL; // cannot wrap wr
114             }
115             if (rb->wr + size == rb->size) {
116                 rb->wr = 0;
117             } else {
118                 // check if we can wrap wr earlier to get space for requested size
119                 if (size > rb->rd - 1) {
120                     return NULL; // cannot wrap wr
121                 }
122                 // shrink buffer a bit, full size will be restored at rd wrapping
123                 rb->cur_size = rb->wr;
124                 rb->wr = 0;
125                 ptr = rb->data;
126                 if (rb->rd == rb->cur_size) {
127                     rb->rd = 0;
128                     if (rb->cur_size < rb->size) {
129                         rb->cur_size = rb->size;
130                     }
131                 }
132                 rb->wr += size;
133             }
134         } else {
135             rb->wr += size;
136         }
137     } else {
138         // |?W......R??|
139         if (size > rb->rd - rb->wr - 1) {
140             return NULL;
141         }
142         rb->wr += size;
143     }
144     return ptr;
145 }
146 
esp_apptrace_rb_consume(esp_apptrace_rb_t * rb,uint32_t size)147 uint8_t *esp_apptrace_rb_consume(esp_apptrace_rb_t *rb, uint32_t size)
148 {
149     uint8_t *ptr = rb->data + rb->rd;
150     if (rb->rd <= rb->wr) {
151         // |?R......W??|
152         if (rb->rd + size > rb->wr) {
153             return NULL;
154         }
155         rb->rd += size;
156     } else {
157         // |?W......R??|
158         if (rb->rd + size > rb->cur_size) {
159             return NULL;
160         } else if (rb->rd + size == rb->cur_size) {
161             // restore full size usage
162             if (rb->cur_size < rb->size) {
163                 rb->cur_size = rb->size;
164             }
165             rb->rd = 0;
166         } else {
167             rb->rd += size;
168         }
169     }
170     return ptr;
171 }
172 
esp_apptrace_rb_read_size_get(esp_apptrace_rb_t * rb)173 uint32_t esp_apptrace_rb_read_size_get(esp_apptrace_rb_t *rb)
174 {
175     uint32_t size = 0;
176     if (rb->rd <= rb->wr) {
177         // |?R......W??|
178         size = rb->wr - rb->rd;
179     } else {
180         // |?W......R??|
181         size = rb->cur_size - rb->rd;
182     }
183     return size;
184 }
185 
esp_apptrace_rb_write_size_get(esp_apptrace_rb_t * rb)186 uint32_t esp_apptrace_rb_write_size_get(esp_apptrace_rb_t *rb)
187 {
188     uint32_t size = 0;
189     if (rb->rd <= rb->wr) {
190         // |?R......W??|
191         size = rb->size - rb->wr;
192         if (size && rb->rd == 0) {
193             size--;
194         }
195     } else {
196         // |?W......R??|
197         size = rb->rd - rb->wr - 1;
198     }
199     return size;
200 }
201