| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- /*
- Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC)
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- Original Author: Shay Gal-on
- */
- #include "coremark.h"
- /*
- Topic: Description
- Benchmark using a linked list.
- Linked list is a common data structure used in many applications.
-
- For our purposes, this will excercise the memory units of the processor.
- In particular, usage of the list pointers to find and alter data.
-
- We are not using Malloc since some platforms do not support this library.
-
- Instead, the memory block being passed in is used to create a list,
- and the benchmark takes care not to add more items then can be
- accomodated by the memory block. The porting layer will make sure
- that we have a valid memory block.
-
- All operations are done in place, without using any extra memory.
-
- The list itself contains list pointers and pointers to data items.
- Data items contain the following:
-
- idx - An index that captures the initial order of the list.
- data - Variable data initialized based on the input parameters. The 16b are divided as follows:
- o Upper 8b are backup of original data.
- o Bit 7 indicates if the lower 7 bits are to be used as is or calculated.
- o Bits 0-2 indicate type of operation to perform to get a 7b value.
- o Bits 3-6 provide input for the operation.
-
- */
- /* local functions */
- list_head *core_list_find(list_head *list,list_data *info);
- list_head *core_list_reverse(list_head *list);
- list_head *core_list_remove(list_head *item);
- list_head *core_list_undo_remove(list_head *item_removed, list_head *item_modified);
- list_head *core_list_insert_new(list_head *insert_point
- , list_data *info, list_head **memblock, list_data **datablock
- , list_head *memblock_end, list_data *datablock_end);
- typedef ee_s32(*list_cmp)(list_data *a, list_data *b, core_results *res);
- list_head *core_list_mergesort(list_head *list, list_cmp cmp, core_results *res);
- ee_s16 calc_func(ee_s16 *pdata, core_results *res) {
- ee_s16 data=*pdata;
- ee_s16 retval;
- ee_u8 optype=(data>>7) & 1; /* bit 7 indicates if the function result has been cached */
- if (optype) /* if cached, use cache */
- return (data & 0x007f);
- else { /* otherwise calculate and cache the result */
- ee_s16 flag=data & 0x7; /* bits 0-2 is type of function to perform */
- ee_s16 dtype=((data>>3) & 0xf); /* bits 3-6 is specific data for the operation */
- dtype |= dtype << 4; /* replicate the lower 4 bits to get an 8b value */
- switch (flag) {
- case 0:
- if (dtype<0x22) /* set min period for bit corruption */
- dtype=0x22;
- retval=core_bench_state(res->size,res->memblock[3],res->seed1,res->seed2,dtype,res->crc);
- if (res->crcstate==0)
- res->crcstate=retval;
- break;
- case 1:
- retval=core_bench_matrix(&(res->mat),dtype,res->crc);
- if (res->crcmatrix==0)
- res->crcmatrix=retval;
- break;
- default:
- retval=data;
- break;
- }
- res->crc=crcu16(retval,res->crc);
- retval &= 0x007f;
- *pdata = (data & 0xff00) | 0x0080 | retval; /* cache the result */
- return retval;
- }
- }
- /* Function: cmp_complex
- Compare the data item in a list cell.
- Can be used by mergesort.
- */
- ee_s32 cmp_complex(list_data *a, list_data *b, core_results *res) {
- ee_s16 val1=calc_func(&(a->data16),res);
- ee_s16 val2=calc_func(&(b->data16),res);
- return val1 - val2;
- }
- /* Function: cmp_idx
- Compare the idx item in a list cell, and regen the data.
- Can be used by mergesort.
- */
- ee_s32 cmp_idx(list_data *a, list_data *b, core_results *res) {
- if (res==NULL) {
- a->data16 = (a->data16 & 0xff00) | (0x00ff & (a->data16>>8));
- b->data16 = (b->data16 & 0xff00) | (0x00ff & (b->data16>>8));
- }
- return a->idx - b->idx;
- }
- void copy_info(list_data *to,list_data *from) {
- to->data16=from->data16;
- to->idx=from->idx;
- }
- /* Benchmark for linked list:
- - Try to find multiple data items.
- - List sort
- - Operate on data from list (crc)
- - Single remove/reinsert
- * At the end of this function, the list is back to original state
- */
- ee_u16 core_bench_list(core_results *res, ee_s16 finder_idx) {
- ee_u16 retval=0;
- ee_u16 found=0,missed=0;
- list_head *list=res->list;
- ee_s16 find_num=res->seed3;
- list_head *this_find;
- list_head *finder, *remover;
- list_data info;
- ee_s16 i;
- info.idx=finder_idx;
- /* find <find_num> values in the list, and change the list each time (reverse and cache if value found) */
- for (i=0; i<find_num; i++) {
- info.data16= (i & 0xff) ;
- this_find=core_list_find(list,&info);
- list=core_list_reverse(list);
- if (this_find==NULL) {
- missed++;
- retval+=(list->next->info->data16 >> 8) & 1;
- }
- else {
- found++;
- if (this_find->info->data16 & 0x1) /* use found value */
- retval+=(this_find->info->data16 >> 9) & 1;
- /* and cache next item at the head of the list (if any) */
- if (this_find->next != NULL) {
- finder = this_find->next;
- this_find->next = finder->next;
- finder->next=list->next;
- list->next=finder;
- }
- }
- if (info.idx>=0)
- info.idx++;
- #if CORE_DEBUG
- ee_printf("List find %d: [%d,%d,%d]\n",i,retval,missed,found);
- #endif
- }
- retval+=found*4-missed;
- /* sort the list by data content and remove one item*/
- if (finder_idx>0)
- list=core_list_mergesort(list,cmp_complex,res);
- remover=core_list_remove(list->next);
- /* CRC data content of list from location of index N forward, and then undo remove */
- finder=core_list_find(list,&info);
- if (!finder)
- finder=list->next;
- while (finder) {
- retval=crc16(list->info->data16,retval);
- finder=finder->next;
- }
- #if CORE_DEBUG
- ee_printf("List sort 1: %04x\n",retval);
- #endif
- remover=core_list_undo_remove(remover,list->next);
- /* sort the list by index, in effect returning the list to original state */
- list=core_list_mergesort(list,cmp_idx,NULL);
- /* CRC data content of list */
- finder=list->next;
- while (finder) {
- retval=crc16(list->info->data16,retval);
- finder=finder->next;
- }
- #if CORE_DEBUG
- ee_printf("List sort 2: %04x\n",retval);
- #endif
- return retval;
- }
- /* Function: core_list_init
- Initialize list with data.
- Parameters:
- blksize - Size of memory to be initialized.
- memblock - Pointer to memory block.
- seed - Actual values chosen depend on the seed parameter.
- The seed parameter MUST be supplied from a source that cannot be determined at compile time
- Returns:
- Pointer to the head of the list.
- */
- list_head *core_list_init(ee_u32 blksize, list_head *memblock, ee_s16 seed) {
- /* calculated pointers for the list */
- ee_u32 per_item=16+sizeof(struct list_data_s);
- ee_u32 size=(blksize/per_item)-2; /* to accomodate systems with 64b pointers, and make sure same code is executed, set max list elements */
- list_head *memblock_end=memblock+size;
- list_data *datablock=(list_data *)(memblock_end);
- list_data *datablock_end=datablock+size;
- /* some useful variables */
- ee_u32 i;
- list_head *finder,*list=memblock;
- list_data info;
- /* create a fake items for the list head and tail */
- list->next=NULL;
- list->info=datablock;
- list->info->idx=0x0000;
- list->info->data16=(ee_s16)0x8080;
- memblock++;
- datablock++;
- info.idx=0x7fff;
- info.data16=(ee_s16)0xffff;
- core_list_insert_new(list,&info,&memblock,&datablock,memblock_end,datablock_end);
-
- /* then insert size items */
- for (i=0; i<size; i++) {
- ee_u16 datpat=((ee_u16)(seed^i) & 0xf);
- ee_u16 dat=(datpat<<3) | (i&0x7); /* alternate between algorithms */
- info.data16=(dat<<8) | dat; /* fill the data with actual data and upper bits with rebuild value */
- core_list_insert_new(list,&info,&memblock,&datablock,memblock_end,datablock_end);
- }
- /* and now index the list so we know initial seed order of the list */
- finder=list->next;
- i=1;
- while (finder->next!=NULL) {
- if (i<size/5) /* first 20% of the list in order */
- finder->info->idx=i++;
- else {
- ee_u16 pat=(ee_u16)(i++ ^ seed); /* get a pseudo random number */
- finder->info->idx=0x3fff & (((i & 0x07) << 8) | pat); /* make sure the mixed items end up after the ones in sequence */
- }
- finder=finder->next;
- }
- list = core_list_mergesort(list,cmp_idx,NULL);
- #if CORE_DEBUG
- ee_printf("Initialized list:\n");
- finder=list;
- while (finder) {
- ee_printf("[%04x,%04x]",finder->info->idx,(ee_u16)finder->info->data16);
- finder=finder->next;
- }
- ee_printf("\n");
- #endif
- return list;
- }
- /* Function: core_list_insert
- Insert an item to the list
- Parameters:
- insert_point - where to insert the item.
- info - data for the cell.
- memblock - pointer for the list header
- datablock - pointer for the list data
- memblock_end - end of region for list headers
- datablock_end - end of region for list data
- Returns:
- Pointer to new item.
- */
- list_head *core_list_insert_new(list_head *insert_point, list_data *info, list_head **memblock, list_data **datablock
- , list_head *memblock_end, list_data *datablock_end) {
- list_head *newitem;
-
- if ((*memblock+1) >= memblock_end)
- return NULL;
- if ((*datablock+1) >= datablock_end)
- return NULL;
-
- newitem=*memblock;
- (*memblock)++;
- newitem->next=insert_point->next;
- insert_point->next=newitem;
-
- newitem->info=*datablock;
- (*datablock)++;
- copy_info(newitem->info,info);
-
- return newitem;
- }
- /* Function: core_list_remove
- Remove an item from the list.
- Operation:
- For a singly linked list, remove by copying the data from the next item
- over to the current cell, and unlinking the next item.
- Note:
- since there is always a fake item at the end of the list, no need to check for NULL.
- Returns:
- Removed item.
- */
- list_head *core_list_remove(list_head *item) {
- list_data *tmp;
- list_head *ret=item->next;
- /* swap data pointers */
- tmp=item->info;
- item->info=ret->info;
- ret->info=tmp;
- /* and eliminate item */
- item->next=item->next->next;
- ret->next=NULL;
- return ret;
- }
- /* Function: core_list_undo_remove
- Undo a remove operation.
- Operation:
- Since we want each iteration of the benchmark to be exactly the same,
- we need to be able to undo a remove.
- Link the removed item back into the list, and switch the info items.
- Parameters:
- item_removed - Return value from the <core_list_remove>
- item_modified - List item that was modified during <core_list_remove>
- Returns:
- The item that was linked back to the list.
-
- */
- list_head *core_list_undo_remove(list_head *item_removed, list_head *item_modified) {
- list_data *tmp;
- /* swap data pointers */
- tmp=item_removed->info;
- item_removed->info=item_modified->info;
- item_modified->info=tmp;
- /* and insert item */
- item_removed->next=item_modified->next;
- item_modified->next=item_removed;
- return item_removed;
- }
- /* Function: core_list_find
- Find an item in the list
- Operation:
- Find an item by idx (if not 0) or specific data value
- Parameters:
- list - list head
- info - idx or data to find
- Returns:
- Found item, or NULL if not found.
- */
- list_head *core_list_find(list_head *list,list_data *info) {
- if (info->idx>=0) {
- while (list && (list->info->idx != info->idx))
- list=list->next;
- return list;
- } else {
- while (list && ((list->info->data16 & 0xff) != info->data16))
- list=list->next;
- return list;
- }
- }
- /* Function: core_list_reverse
- Reverse a list
- Operation:
- Rearrange the pointers so the list is reversed.
- Parameters:
- list - list head
- info - idx or data to find
- Returns:
- Found item, or NULL if not found.
- */
- list_head *core_list_reverse(list_head *list) {
- list_head *next=NULL, *tmp;
- while (list) {
- tmp=list->next;
- list->next=next;
- next=list;
- list=tmp;
- }
- return next;
- }
- /* Function: core_list_mergesort
- Sort the list in place without recursion.
- Description:
- Use mergesort, as for linked list this is a realistic solution.
- Also, since this is aimed at embedded, care was taken to use iterative rather then recursive algorithm.
- The sort can either return the list to original order (by idx) ,
- or use the data item to invoke other other algorithms and change the order of the list.
- Parameters:
- list - list to be sorted.
- cmp - cmp function to use
- Returns:
- New head of the list.
- Note:
- We have a special header for the list that will always be first,
- but the algorithm could theoretically modify where the list starts.
- */
- list_head *core_list_mergesort(list_head *list, list_cmp cmp, core_results *res) {
- list_head *p, *q, *e, *tail;
- ee_s32 insize, nmerges, psize, qsize, i;
- insize = 1;
- while (1) {
- p = list;
- list = NULL;
- tail = NULL;
- nmerges = 0; /* count number of merges we do in this pass */
- while (p) {
- nmerges++; /* there exists a merge to be done */
- /* step `insize' places along from p */
- q = p;
- psize = 0;
- for (i = 0; i < insize; i++) {
- psize++;
- q = q->next;
- if (!q) break;
- }
- /* if q hasn't fallen off end, we have two lists to merge */
- qsize = insize;
- /* now we have two lists; merge them */
- while (psize > 0 || (qsize > 0 && q)) {
- /* decide whether next element of merge comes from p or q */
- if (psize == 0) {
- /* p is empty; e must come from q. */
- e = q; q = q->next; qsize--;
- } else if (qsize == 0 || !q) {
- /* q is empty; e must come from p. */
- e = p; p = p->next; psize--;
- } else if (cmp(p->info,q->info,res) <= 0) {
- /* First element of p is lower (or same); e must come from p. */
- e = p; p = p->next; psize--;
- } else {
- /* First element of q is lower; e must come from q. */
- e = q; q = q->next; qsize--;
- }
- /* add the next element to the merged list */
- if (tail) {
- tail->next = e;
- } else {
- list = e;
- }
- tail = e;
- }
- /* now p has stepped `insize' places along, and q has too */
- p = q;
- }
-
- tail->next = NULL;
- /* If we have done only one merge, we're finished. */
- if (nmerges <= 1) /* allow for nmerges==0, the empty list case */
- return list;
- /* Otherwise repeat, merging lists twice the size */
- insize *= 2;
- }
- #if COMPILER_REQUIRES_SORT_RETURN
- return list;
- #endif
- }
|