test_ringbuf.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "freertos/FreeRTOS.h"
  4. #include "freertos/task.h"
  5. #include "freertos/queue.h"
  6. #include "freertos/semphr.h"
  7. #include "freertos/ringbuf.h"
  8. #include "driver/timer.h"
  9. #include "esp_spi_flash.h"
  10. #include "unity.h"
  11. //Definitions used in multiple test cases
  12. #define TIMEOUT_TICKS 10
  13. #define NO_OF_RB_TYPES 3
  14. #define ITEM_HDR_SIZE 8
  15. #define SMALL_ITEM_SIZE 8
  16. #define LARGE_ITEM_SIZE (2 * SMALL_ITEM_SIZE)
  17. #define BUFFER_SIZE 160 //4Byte aligned size
  18. static const uint8_t small_item[SMALL_ITEM_SIZE] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
  19. static const uint8_t large_item[LARGE_ITEM_SIZE] = { 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
  20. 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
  21. static RingbufHandle_t buffer_handles[NO_OF_RB_TYPES];
  22. static SemaphoreHandle_t done_sem;
  23. static void send_item_and_check(RingbufHandle_t handle, const uint8_t *item, size_t item_size, TickType_t ticks_to_wait, bool in_isr)
  24. {
  25. BaseType_t ret;
  26. if (in_isr) {
  27. ret = xRingbufferSendFromISR(handle, (void *)item, item_size, NULL);
  28. } else {
  29. ret = xRingbufferSend(handle, (void *)item, item_size, ticks_to_wait);
  30. }
  31. TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to send item");
  32. }
  33. static void receive_check_and_return_item_no_split(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr)
  34. {
  35. //Receive item from no-split buffer
  36. size_t item_size;
  37. uint8_t *item;
  38. if (in_isr) {
  39. item = (uint8_t *)xRingbufferReceiveFromISR(handle, &item_size);
  40. } else {
  41. item = (uint8_t *)xRingbufferReceive(handle, &item_size, ticks_to_wait);
  42. }
  43. TEST_ASSERT_MESSAGE(item != NULL, "Failed to receive item");
  44. TEST_ASSERT_MESSAGE(item_size == expected_size, "Item size is incorrect");
  45. //Check data of received item
  46. for (int i = 0; i < item_size; i++) {
  47. TEST_ASSERT_MESSAGE(item[i] == expected_data[i], "Item data is invalid");
  48. }
  49. //Return item
  50. if (in_isr) {
  51. vRingbufferReturnItemFromISR(handle, (void *)item, NULL);
  52. } else {
  53. vRingbufferReturnItem(handle, (void *)item);
  54. }
  55. }
  56. static void receive_check_and_return_item_allow_split(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr)
  57. {
  58. //Receive item
  59. size_t item_size1, item_size2;
  60. uint8_t *item1, *item2;
  61. BaseType_t ret;
  62. if (in_isr) {
  63. ret = xRingbufferReceiveSplitFromISR(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2);
  64. } else {
  65. ret = xRingbufferReceiveSplit(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2, ticks_to_wait);
  66. }
  67. //= xRingbufferReceiveSplit(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2, ticks_to_wait);
  68. TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to receive item");
  69. TEST_ASSERT_MESSAGE(item1 != NULL, "Failed to receive item");
  70. //Check data of received item(s) and return them
  71. if (item2 == NULL) {
  72. TEST_ASSERT_MESSAGE(item_size1 == expected_size, "Item size is incorrect");
  73. for (int i = 0; i < item_size1; i++) {
  74. TEST_ASSERT_MESSAGE(item1[i] == expected_data[i], "Item data is invalid");
  75. }
  76. //Return item
  77. if (in_isr) {
  78. vRingbufferReturnItemFromISR(handle, (void *)item1, NULL);
  79. } else {
  80. vRingbufferReturnItem(handle, (void *)item1);
  81. }
  82. } else {
  83. //Item was split
  84. TEST_ASSERT_MESSAGE(item_size1 + item_size2 == expected_size, "Total item size is incorrect");
  85. for (int i = 0; i < item_size1; i++) {
  86. TEST_ASSERT_MESSAGE(item1[i] == expected_data[i], "Head item data is invalid");
  87. }
  88. for (int i = 0; i < item_size2; i++) {
  89. TEST_ASSERT_MESSAGE(item2[i] == expected_data[item_size1 + i], "Head item data is invalid");
  90. }
  91. //Return Items
  92. if (in_isr) {
  93. vRingbufferReturnItemFromISR(handle, (void *)item1, NULL);
  94. vRingbufferReturnItemFromISR(handle, (void *)item2, NULL);
  95. } else {
  96. vRingbufferReturnItem(handle, (void *)item1);
  97. vRingbufferReturnItem(handle, (void *)item2);
  98. }
  99. }
  100. }
  101. static void receive_check_and_return_item_byte_buffer(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr)
  102. {
  103. //Receive item
  104. size_t item_size;
  105. uint8_t *item;
  106. if (in_isr) {
  107. item = (uint8_t *)xRingbufferReceiveUpToFromISR(handle, &item_size, expected_size);
  108. } else {
  109. item = (uint8_t *)xRingbufferReceiveUpTo(handle, &item_size, ticks_to_wait, expected_size); //Limit amount of bytes returned to the size of one item
  110. }
  111. TEST_ASSERT_MESSAGE(item != NULL, "Failed to receive item");
  112. //Check data of received item
  113. for (int i = 0; i < item_size; i++) {
  114. TEST_ASSERT_MESSAGE(item[i] == expected_data[i], "Item data is invalid");
  115. }
  116. //Return item
  117. if (in_isr) {
  118. vRingbufferReturnItemFromISR(handle, (void *)item, NULL);
  119. } else {
  120. vRingbufferReturnItem(handle, (void *)item);
  121. }
  122. //Check if item wrapped around
  123. if (item_size < expected_size) {
  124. //Item is wrapped, receive second portion
  125. size_t item_size2;
  126. uint8_t *item2;
  127. if (in_isr) {
  128. item2 = (uint8_t *)xRingbufferReceiveUpToFromISR(handle, &item_size2, expected_size - item_size);
  129. } else {
  130. item2 = (uint8_t *)xRingbufferReceiveUpTo(handle, &item_size2, ticks_to_wait, expected_size - item_size);
  131. }
  132. //= (uint8_t *)xRingbufferReceiveUpTo(handle, &item_size2, ticks_to_wait, expected_size - item_size);
  133. TEST_ASSERT_MESSAGE(item2 != NULL, "Failed to receive item");
  134. TEST_ASSERT_MESSAGE(item_size + item_size2 == expected_size, "Total item size is incorrect");
  135. for (int i = 0; i < item_size2; i++) {
  136. TEST_ASSERT_MESSAGE(item2[i] == expected_data[item_size + i], "Item data is invalid");
  137. }
  138. if (in_isr) {
  139. vRingbufferReturnItemFromISR(handle, (void *)item2, NULL);
  140. } else {
  141. vRingbufferReturnItem(handle, (void *)item2);
  142. }
  143. } else {
  144. TEST_ASSERT_MESSAGE(item_size == expected_size, "Item size is incorrect");
  145. }
  146. }
  147. /* ----------------- Basic ring buffer behavior tests cases --------------------
  148. * The following test cases will test basic send, receive, and wrap around
  149. * behavior of each type of ring buffer. Each test case will do the following
  150. * 1) Send multiple items (nearly fill the buffer)
  151. * 2) Receive and check the sent items (also prepares the buffer for a wrap around
  152. * 3) Send a final item that causes a wrap around
  153. * 4) Receive and check the wrapped item
  154. */
  155. TEST_CASE("Test ring buffer No-Split", "[freertos]")
  156. {
  157. //Create buffer
  158. RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT);
  159. TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
  160. //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
  161. int no_of_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + SMALL_ITEM_SIZE)) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE);
  162. //Test sending items
  163. for (int i = 0; i < no_of_items; i++) {
  164. send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
  165. }
  166. //Test receiving items
  167. for (int i = 0; i < no_of_items; i++) {
  168. receive_check_and_return_item_no_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
  169. }
  170. //Write pointer should be near the end, test wrap around
  171. uint32_t write_pos_before, write_pos_after;
  172. vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL);
  173. //Send large item that causes wrap around
  174. send_item_and_check(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
  175. //Receive wrapped item
  176. receive_check_and_return_item_no_split(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
  177. vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_after, NULL);
  178. TEST_ASSERT_MESSAGE(write_pos_after < write_pos_before, "Failed to wrap around");
  179. //Cleanup
  180. vRingbufferDelete(buffer_handle);
  181. }
  182. TEST_CASE("Test ring buffer Allow-Split", "[freertos]")
  183. {
  184. //Create buffer
  185. RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_ALLOWSPLIT);
  186. TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
  187. //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
  188. int no_of_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + SMALL_ITEM_SIZE)) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE);
  189. //Test sending items
  190. for (int i = 0; i < no_of_items; i++) {
  191. send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
  192. }
  193. //Test receiving items
  194. for (int i = 0; i < no_of_items; i++) {
  195. receive_check_and_return_item_allow_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
  196. }
  197. //Write pointer should be near the end, test wrap around
  198. uint32_t write_pos_before, write_pos_after;
  199. vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL);
  200. //Send large item that causes wrap around
  201. send_item_and_check(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
  202. //Receive wrapped item
  203. receive_check_and_return_item_allow_split(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
  204. vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_after, NULL);
  205. TEST_ASSERT_MESSAGE(write_pos_after < write_pos_before, "Failed to wrap around");
  206. //Cleanup
  207. vRingbufferDelete(buffer_handle);
  208. }
  209. TEST_CASE("Test ring buffer Byte Buffer", "[freertos]")
  210. {
  211. //Create buffer
  212. RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_BYTEBUF);
  213. TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
  214. //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
  215. int no_of_items = (BUFFER_SIZE - SMALL_ITEM_SIZE) / SMALL_ITEM_SIZE;
  216. //Test sending items
  217. for (int i = 0; i < no_of_items; i++) {
  218. send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
  219. }
  220. //Test receiving items
  221. for (int i = 0; i < no_of_items; i++) {
  222. receive_check_and_return_item_byte_buffer(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
  223. }
  224. //Write pointer should be near the end, test wrap around
  225. uint32_t write_pos_before, write_pos_after;
  226. vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL);
  227. //Send large item that causes wrap around
  228. send_item_and_check(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
  229. //Receive wrapped item
  230. receive_check_and_return_item_byte_buffer(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
  231. vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_after, NULL);
  232. TEST_ASSERT_MESSAGE(write_pos_after < write_pos_before, "Failed to wrap around");
  233. //Cleanup
  234. vRingbufferDelete(buffer_handle);
  235. }
  236. /* ----------------------- Ring buffer queue sets test ------------------------
  237. * The following test case will test receiving from ring buffers that have been
  238. * added to a queue set. The test case will do the following...
  239. * 1) Ring buffer of each type is created and added to the queue set
  240. * 2) A receiving task is created to select from the queue set and read from the appropriate ring buffer
  241. */
  242. static void queue_set_receiving_task(void *queue_set_handle)
  243. {
  244. QueueSetHandle_t queue_set = (QueueSetHandle_t)queue_set_handle;
  245. //Receive multiple items via queue set
  246. BaseType_t done = pdFALSE;
  247. int no_of_items = BUFFER_SIZE / SMALL_ITEM_SIZE;
  248. int items_rec_count[NO_OF_RB_TYPES] = {0};
  249. while (done != pdTRUE) {
  250. xQueueSetMemberHandle member = xQueueSelectFromSet(queue_set, TIMEOUT_TICKS);
  251. //Read from selected ring buffer
  252. if (xRingbufferCanRead(buffer_handles[0], member) == pdTRUE) {
  253. //No-split buffer
  254. receive_check_and_return_item_no_split(buffer_handles[0], small_item, SMALL_ITEM_SIZE, 0, false);
  255. items_rec_count[0] ++;
  256. } else if (xRingbufferCanRead(buffer_handles[1], member) == pdTRUE) {
  257. //Allow-split buffer
  258. receive_check_and_return_item_allow_split(buffer_handles[1], small_item, SMALL_ITEM_SIZE, 0, false);
  259. items_rec_count[1] ++;
  260. } else if (xRingbufferCanRead(buffer_handles[2], member) == pdTRUE){
  261. //Byte buffer
  262. receive_check_and_return_item_byte_buffer(buffer_handles[2], small_item, SMALL_ITEM_SIZE, 0, false);
  263. items_rec_count[2] ++;
  264. } else {
  265. TEST_ASSERT_MESSAGE( false, "Error with queue set member");
  266. }
  267. //Check for completion
  268. if (items_rec_count[0] == no_of_items &&
  269. items_rec_count[1] == no_of_items &&
  270. items_rec_count[2] == no_of_items) {
  271. done = pdTRUE;
  272. }
  273. }
  274. xSemaphoreGive(done_sem);
  275. vTaskDelete(NULL);
  276. }
  277. TEST_CASE("Test ring buffer with queue sets", "[freertos]")
  278. {
  279. QueueSetHandle_t queue_set = xQueueCreateSet(NO_OF_RB_TYPES);
  280. done_sem = xSemaphoreCreateBinary();
  281. //Create ring buffer of each type, then add them to a queue set
  282. for (int i = 0; i < NO_OF_RB_TYPES; i++) {
  283. buffer_handles[i] = xRingbufferCreate(BUFFER_SIZE, i);
  284. TEST_ASSERT_MESSAGE(buffer_handles[i] != NULL, "Failed to create ring buffer");
  285. TEST_ASSERT_MESSAGE(xRingbufferAddToQueueSetRead(buffer_handles[i], queue_set) == pdPASS, "Failed to add to read queue set");
  286. }
  287. //Create a task to send items to each ring buffer
  288. int no_of_items = BUFFER_SIZE / SMALL_ITEM_SIZE;
  289. xTaskCreatePinnedToCore(queue_set_receiving_task, "rec tsk", 2048, (void *)queue_set, UNITY_FREERTOS_PRIORITY + 1 , NULL, 0);
  290. //Send multiple items to each type of ring buffer
  291. for (int i = 0; i < no_of_items; i++) {
  292. for (int j = 0; j < NO_OF_RB_TYPES; j++) {
  293. send_item_and_check(buffer_handles[j], small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
  294. }
  295. }
  296. xSemaphoreTake(done_sem, portMAX_DELAY);
  297. vSemaphoreDelete(done_sem);
  298. //Remove and delete ring buffers from queue sets
  299. for (int i = 0; i < NO_OF_RB_TYPES; i++) {
  300. TEST_ASSERT_MESSAGE(xRingbufferRemoveFromQueueSetRead(buffer_handles[i], queue_set) == pdTRUE, "Failed to remove from read queue set");
  301. vRingbufferDelete(buffer_handles[i]);
  302. }
  303. vQueueDelete(queue_set);
  304. }
  305. /* -------------------------- Test ring buffer ISR -----------------------------
  306. * The following test case tests ring buffer ISR API. A timer is used to trigger
  307. * the ISR. The test case will do the following
  308. * 1) ISR will be triggered periodically by timer
  309. * 2) The ISR will iterate through all ring buffer types where each iteration
  310. * will send then receive an item to a ring buffer.
  311. */
  312. #define TIMER_GROUP 0
  313. #define TIMER_NUMBER 0
  314. #define ISR_ITERATIONS ((BUFFER_SIZE / SMALL_ITEM_SIZE) * 2)
  315. intr_handle_t ringbuffer_isr_handle;
  316. static int buf_type;
  317. static int iterations;
  318. static void ringbuffer_isr(void *arg)
  319. {
  320. //Clear timer interrupt
  321. TIMERG0.int_clr_timers.t0 = 1;
  322. TIMERG0.hw_timer[xPortGetCoreID()].config.alarm_en = 1;
  323. //Test sending to buffer from ISR from ISR
  324. if (buf_type < NO_OF_RB_TYPES) {
  325. send_item_and_check(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
  326. }
  327. //Receive item from ISR
  328. if (buf_type == RINGBUF_TYPE_NOSPLIT) {
  329. //Test receive from ISR for no-split buffer
  330. receive_check_and_return_item_no_split(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
  331. buf_type++;
  332. } else if (buf_type == RINGBUF_TYPE_ALLOWSPLIT) {
  333. //Test send from ISR to allow-split buffer
  334. receive_check_and_return_item_allow_split(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
  335. buf_type++;
  336. } else if (buf_type == RINGBUF_TYPE_BYTEBUF) {
  337. //Test receive from ISR for byte buffer
  338. receive_check_and_return_item_byte_buffer(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
  339. buf_type++;
  340. } else if (buf_type == NO_OF_RB_TYPES) {
  341. //Check if all iterations complete
  342. if (iterations < ISR_ITERATIONS) {
  343. iterations++;
  344. buf_type = 0; //Reset and iterate through each buffer type again
  345. return;
  346. } else {
  347. //Signal complete
  348. BaseType_t task_woken = pdFALSE;
  349. xSemaphoreGiveFromISR(done_sem, &task_woken);
  350. if (task_woken == pdTRUE) {
  351. buf_type++;
  352. portYIELD_FROM_ISR();
  353. }
  354. }
  355. }
  356. }
  357. static void setup_timer()
  358. {
  359. //Setup timer for ISR
  360. int timer_group = TIMER_GROUP;
  361. int timer_idx = TIMER_NUMBER;
  362. timer_config_t config;
  363. config.alarm_en = 1;
  364. config.auto_reload = 1;
  365. config.counter_dir = TIMER_COUNT_UP;
  366. config.divider = 10000;
  367. config.intr_type = TIMER_INTR_LEVEL;
  368. config.counter_en = TIMER_PAUSE;
  369. timer_init(timer_group, timer_idx, &config); //Configure timer
  370. timer_pause(timer_group, timer_idx); //Stop timer counter
  371. timer_set_counter_value(timer_group, timer_idx, 0x00000000ULL); //Load counter value
  372. timer_set_alarm_value(timer_group, timer_idx, 20); //Set alarm value
  373. timer_enable_intr(timer_group, timer_idx); //Enable timer interrupt
  374. timer_set_auto_reload(timer_group, timer_idx, 1); //Auto Reload
  375. timer_isr_register(timer_group, timer_idx, ringbuffer_isr, NULL, 0, &ringbuffer_isr_handle); //Set ISR handler
  376. }
  377. static void cleanup_timer()
  378. {
  379. timer_disable_intr(TIMER_GROUP, TIMER_NUMBER);
  380. esp_intr_free(ringbuffer_isr_handle);
  381. }
  382. TEST_CASE("Test ring buffer ISR", "[freertos]")
  383. {
  384. for (int i = 0; i < NO_OF_RB_TYPES; i++) {
  385. buffer_handles[i] = xRingbufferCreate(BUFFER_SIZE, i);
  386. }
  387. done_sem = xSemaphoreCreateBinary();
  388. buf_type = 0;
  389. iterations = 0;
  390. setup_timer();
  391. //Start timer to trigger ISR
  392. timer_start(TIMER_GROUP, TIMER_NUMBER);
  393. //Wait for ISR to complete multiple iterations
  394. xSemaphoreTake(done_sem, portMAX_DELAY);
  395. //Cleanup
  396. cleanup_timer();
  397. vSemaphoreDelete(done_sem);
  398. for (int i = 0; i < NO_OF_RB_TYPES; i++) {
  399. vRingbufferDelete(buffer_handles[i]);
  400. }
  401. }
  402. /* ---------------------------- Test ring buffer SMP ---------------------------
  403. * The following test case tests each type of ring buffer in an SMP fashion. A
  404. * sending task and a receiving task is created. The sending task will split
  405. * a continuous piece of data into items of random length and send it to a ring
  406. * buffer. The receiving task will receive and check those items.
  407. * Every permutation of core pinning of the sending and receiving task will be
  408. * tested.
  409. */
  410. #define SRAND_SEED 3 //Arbitrarily chosen srand() seed
  411. #define SMP_TEST_ITERATIONS 4
  412. static const char continuous_data[] = {"A_very_long_string_that_will_be_split_into_"
  413. "items_of_random_lengths_and_sent_to_the_ring_"
  414. "buffer._The_maximum_random_length_will_also_"
  415. "be_increased_over_multiple_iterations_in_this"
  416. "_test"};
  417. #define CONT_DATA_LEN sizeof(continuous_data)
  418. #define CONT_DATA_TEST_BUFF_LEN (CONT_DATA_LEN/2) //This will guarantee that the buffer must do a wrap around at some point
  419. typedef struct {
  420. RingbufHandle_t buffer;
  421. ringbuf_type_t type;
  422. } task_args_t;
  423. static SemaphoreHandle_t tasks_done;
  424. static SemaphoreHandle_t tx_done;
  425. static SemaphoreHandle_t rx_done;
  426. static void send_to_buffer(RingbufHandle_t buffer, size_t max_item_size)
  427. {
  428. for (int iter = 0; iter < SMP_TEST_ITERATIONS; iter++) {
  429. size_t bytes_sent = 0; //Number of data bytes sent in this iteration
  430. size_t next_item_size; //Size of next item to send
  431. while (bytes_sent < CONT_DATA_LEN) {
  432. //Get size of next item
  433. next_item_size = rand() % (max_item_size + 1);
  434. if (next_item_size + bytes_sent > CONT_DATA_LEN) {
  435. next_item_size = CONT_DATA_LEN - bytes_sent;
  436. }
  437. //Send item
  438. TEST_ASSERT_MESSAGE(xRingbufferSend(buffer, (void *)&(continuous_data[bytes_sent]), next_item_size, TIMEOUT_TICKS) == pdTRUE, "Failed to send an item");
  439. bytes_sent += next_item_size;
  440. }
  441. xSemaphoreGive(tx_done);
  442. xSemaphoreTake(rx_done, portMAX_DELAY);
  443. }
  444. }
  445. static void read_from_buffer(RingbufHandle_t buffer, ringbuf_type_t buf_type, size_t max_rec_size)
  446. {
  447. for (int iter = 0; iter < SMP_TEST_ITERATIONS; iter++) {
  448. size_t bytes_rec = 0; //Number of data bytes received in this iteration
  449. while (bytes_rec < CONT_DATA_LEN) {
  450. size_t item_size, item_size2; //Possible for allow split buffers to receive two items
  451. char *item_data, *item_data2;
  452. //Select appropriate receive function for type of ring buffer
  453. if (buf_type == RINGBUF_TYPE_NOSPLIT) {
  454. item_data = (char *)xRingbufferReceive(buffer, &item_size, TIMEOUT_TICKS);
  455. } else if (buf_type == RINGBUF_TYPE_ALLOWSPLIT) {
  456. BaseType_t ret = xRingbufferReceiveSplit(buffer, (void **)&item_data, (void **)&item_data2, &item_size, &item_size2, TIMEOUT_TICKS);
  457. TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to receive any item");
  458. } else {
  459. item_data = (char *)xRingbufferReceiveUpTo(buffer, &item_size, TIMEOUT_TICKS, max_rec_size);
  460. }
  461. //Check received item and return it
  462. TEST_ASSERT_MESSAGE(item_data != NULL, "Failed to receive an item");
  463. if (buf_type == RINGBUF_TYPE_BYTEBUF) {
  464. TEST_ASSERT_MESSAGE(item_size <= max_rec_size, "Received data exceeds max size");
  465. }
  466. for (int i = 0; i < item_size; i++) {
  467. //Check item_data is valid
  468. TEST_ASSERT_MESSAGE(item_data[i] == continuous_data[bytes_rec + i], "Received data is corrupted");
  469. }
  470. bytes_rec += item_size;
  471. vRingbufferReturnItem(buffer, item_data);
  472. if (buf_type == RINGBUF_TYPE_ALLOWSPLIT && item_data2 != NULL) {
  473. //Check item_data2 is valid
  474. for (int i = 0; i < item_size2; i++) {
  475. TEST_ASSERT_MESSAGE(item_data2[i] == continuous_data[bytes_rec + i], "Received split data is corrupted");
  476. }
  477. bytes_rec += item_size2;
  478. vRingbufferReturnItem(buffer, item_data2);
  479. }
  480. }
  481. TEST_ASSERT_MESSAGE(bytes_rec == CONT_DATA_LEN, "Total length of received data is incorrect");
  482. xSemaphoreGive(rx_done);
  483. xSemaphoreTake(tx_done, portMAX_DELAY);
  484. }
  485. }
  486. static void send_task(void *args)
  487. {
  488. RingbufHandle_t buffer = ((task_args_t *)args)->buffer;
  489. size_t max_item_len = xRingbufferGetMaxItemSize(buffer);
  490. //Test sending short length items
  491. send_to_buffer(buffer, 1);
  492. //Test sending mid length items
  493. send_to_buffer(buffer, max_item_len/2);
  494. //Test sending long length items
  495. send_to_buffer(buffer, max_item_len);
  496. vTaskDelete(NULL);
  497. }
  498. static void rec_task(void *args)
  499. {
  500. RingbufHandle_t buffer = ((task_args_t *)args)->buffer;
  501. size_t max_rec_len = xRingbufferGetMaxItemSize(buffer);
  502. //Test receiving short length items
  503. read_from_buffer(buffer, ((task_args_t *)args)->type, 1);
  504. //Test receiving mid length items
  505. read_from_buffer(buffer, ((task_args_t *)args)->type, max_rec_len/2);
  506. //Test receiving long length items
  507. read_from_buffer(buffer, ((task_args_t *)args)->type, max_rec_len);
  508. xSemaphoreGive(tasks_done);
  509. vTaskDelete(NULL);
  510. }
  511. TEST_CASE("Test ring buffer SMP", "[freertos]")
  512. {
  513. ets_printf("size of buf %d\n", CONT_DATA_LEN);
  514. tx_done = xSemaphoreCreateBinary(); //Semaphore to indicate send is done for a particular iteration
  515. rx_done = xSemaphoreCreateBinary(); //Semaphore to indicate receive is done for a particular iteration
  516. tasks_done = xSemaphoreCreateBinary(); //Semaphore used to to indicate send and receive tasks completed running
  517. srand(SRAND_SEED); //Seed RNG
  518. //Iterate through buffer types (No split, split, then byte buff)
  519. for (ringbuf_type_t buf_type = 0; buf_type <= RINGBUF_TYPE_BYTEBUF; buf_type++) {
  520. //Create buffer
  521. task_args_t task_args;
  522. task_args.buffer = xRingbufferCreate(CONT_DATA_TEST_BUFF_LEN, buf_type); //Create buffer of selected type
  523. task_args.type = buf_type;
  524. for (int prior_mod = -1; prior_mod < 2; prior_mod++) { //Test different relative priorities
  525. //Test every permutation of core affinity
  526. for (int send_core = 0; send_core < portNUM_PROCESSORS; send_core++) {
  527. for (int rec_core = 0; rec_core < portNUM_PROCESSORS; rec_core ++) {
  528. ets_printf("Type: %d, PM: %d, SC: %d, RC: %d\n", buf_type, prior_mod, send_core, rec_core);
  529. xTaskCreatePinnedToCore(send_task, "send tsk", 2048, (void *)&task_args, 10 + prior_mod, NULL, send_core);
  530. xTaskCreatePinnedToCore(rec_task, "rec tsk", 2048, (void *)&task_args, 10, NULL, rec_core);
  531. xSemaphoreTake(tasks_done, portMAX_DELAY);
  532. vTaskDelay(5); //Allow idle to clean up
  533. }
  534. }
  535. }
  536. //Delete ring buffer
  537. vRingbufferDelete(task_args.buffer);
  538. vTaskDelay(10);
  539. }
  540. //Cleanup
  541. vSemaphoreDelete(tx_done);
  542. vSemaphoreDelete(rx_done);
  543. vSemaphoreDelete(tasks_done);
  544. }
  545. static IRAM_ATTR __attribute__((noinline)) bool iram_ringbuf_test()
  546. {
  547. bool result = true;
  548. spi_flash_guard_get()->start(); // Disables flash cache
  549. RingbufHandle_t handle = xRingbufferCreate(CONT_DATA_TEST_BUFF_LEN, RINGBUF_TYPE_NOSPLIT);
  550. result = result && (handle != NULL);
  551. xRingbufferGetMaxItemSize(handle);
  552. vRingbufferDelete(handle);
  553. spi_flash_guard_get()->end(); // Re-enables flash cache
  554. return result;
  555. }
  556. TEST_CASE("Test ringbuffer functions work with flash cache disabled", "[freertos]")
  557. {
  558. TEST_ASSERT( iram_ringbuf_test() );
  559. }