-
Notifications
You must be signed in to change notification settings - Fork 9
2....b)Software:③Code Insertion and Replacement
DMM components should be inserted into source code and variables should be declared to store necessary DMM information. In the following, we will present these insertions with concrete examples.
offset: The register offset is an integer variable which records the offset (address) of pointer in the heap. Since the return value of allocation function is an integer value, instead of void pointer (void*) in C standard, it will be stored in this register variable. Besides, the assignment of pointer to pointer is actually the transferring of the offset.
ap_uint<18> offset_now; // HI-DMM insert: offset of pointer now
ap_uint<18> offset_left; // HI-DMM insert: offset of pointer left
ap_uint<18> offset_right; // HI-DMM insert: offset of pointer right
ap_uint<18> offset_tail; // HI-DMM insert: offset of pointer tail
ap_uint<18> offset_local_dis; // HI-DMM insert: offset of pointer local_dis
//involve in the following operations:
//allocation
// HI-DMM replace: local_dis = (int *)malloc(n * sizeof(int));
offset_local_dis = HLS_malloc<8192>(n, Hi_DMM_allocator_1_Super_HTA8k);
// HI-DMM insert: stress offset of pointer local_dis
local_dis = Hi_DMM_dynamic_heap_1 + offset_local_dis;
//de-alloction
// HI-DMM replace: free((unsigned long long)now);
HLS_free<0>(offset_now, size_now, Hi_DMM_allocator_0_KWTA_mini16);
//assign one pointer to another one
// HI-DMM insert for: now = tail; (now and tail are pointers of user-defined struct)
offset_now = offset_tail;
size_now = size_tail;
now = Hi_DMM_dynamic_heap_0 + offset_now; // HI-DMM insert: stress offset of pointer now
size: The register size is an integer variable which records the size of dynamic memory allocated for a pointer in the heap. This register will be set when dynamic memory for the pointer is allocated. When de-allocating a pointer, Hi-DMM allocator will require the size of allocated memory to locate the allocated node in the buddy tree and verify whether the de-allocation can be done. Besides, the assignment of pointer to pointer also involves the transferring of the size register to maintain the completion of pointer information.
ap_uint<18> size_now; // HI-DMM insert: size of pointer now
ap_uint<18> size_left; // HI-DMM insert: size of pointer left
ap_uint<18> size_local_dis; // HI-DMM insert: size of pointer local_dis
//involve in the following operations:
//allocation
// HI-DMM insert: set size of pointer
size_new_node = SIZE_LIST;
// HI-DMM replace: new_node = (LIST *)malloc(sizeof(LIST));
offset_new_node = HLS_malloc<2341>(1, Hi_DMM_allocator_0_KWTA_mini16) * SIZE_LIST;
//de-alloction
// HI-DMM replace: free((unsigned long long)now);
HLS_free<0>(offset_now, size_now, Hi_DMM_allocator_0_KWTA_mini16);
//assign one pointer to another one
// HI-DMM insert for: now = tail; (now and tail are pointers of user-defined struct)
offset_now = offset_tail;
size_now = size_tail;
now = Hi_DMM_dynamic_heap_0 + offset_now; // HI-DMM insert: stress offset of pointer now
user-defined struct: since Hi-DMM supports the dynamic allocation of user-defined struct ("struct" in C), the accesses of the components of these structures are also transformed into expressions based on pointers and offsets. In order to store those user-defined struct in general array of typical type, Hi-DMM record the number of components and uses offsets of filed components to indicate where a particular struct component is stored. They will be involved in the accesses to these struct pointers.
//typedef struct LIST_TYPE
//{
// int VAL;
// LIST_TYPE *NEXT, *PREVIOUS, *LEFT, *RIGHT, *PARENT;
// int IAMLEFT;
//} LIST;
#define SIZE_LIST 7
#define OFFSET_LIST_VAL 0
#define OFFSET_LIST_NEXT 1
#define OFFSET_LIST_PREVIOUS 2
#define OFFSET_LIST_LEFT 3
#define OFFSET_LIST_RIGHT 4
#define OFFSET_LIST_PARENT 5
#define OFFSET_LIST_IAMLEFT 6
//involve in the following operations:
//allocation
// HI-DMM insert: set size of pointer
size_new_node = SIZE_LIST;
// HI-DMM replace: new_node = (LIST *)malloc(sizeof(LIST));
offset_new_node = HLS_malloc<2341>(1, Hi_DMM_allocator_0_KWTA_mini16) * SIZE_LIST;
//access to struct pointer
// HI-DMM replace: head->VAL = data[0];
head[OFFSET_LIST_VAL] = data[0];
head = Hi_DMM_dynamic_heap_0 + offset_head; // HI-DMM insert: stress offset of pointer head
// HI-DMM replace: head->NEXT = NULL;
head[OFFSET_LIST_NEXT] = NULL;
head = Hi_DMM_dynamic_heap_0 + offset_head; // HI-DMM insert: stress offset of pointer head
// HI-DMM replace: head->PREVIOUS = NULL;
head[OFFSET_LIST_PREVIOUS] = NULL;
head = Hi_DMM_dynamic_heap_0 + offset_head; // HI-DMM insert: stress offset of pointer head
// HI-DMM replace: head->PARENT = NULL;
head[OFFSET_LIST_PARENT] = NULL;
head = Hi_DMM_dynamic_heap_0 + offset_head; // HI-DMM insert: stress offset of pointer head
// HI-DMM replace: head->LEFT = NULL;
head[OFFSET_LIST_LEFT] = NULL;
head = Hi_DMM_dynamic_heap_0 + offset_head; // HI-DMM insert: stress offset of pointer head
// HI-DMM replace: head->RIGHT = NULL;
head[OFFSET_LIST_RIGHT] = NULL;
head = Hi_DMM_dynamic_heap_0 + offset_head; // HI-DMM insert: stress offset of pointer head
// HI-DMM replace: head->IAMLEFT = -1;
head[OFFSET_LIST_IAMLEFT] = -1;
(de-)allocation function and Hi-DMM signal ports: These functions and signals handle the communication between accelerators and allocators, like sending requests and processing response. For more details, user can refer to the interconnection and interaction mentioned in hardware design of Hi-DMM
typedef struct
{
int size;
int addr;
int free_target;
char cmd;
} hidmm_alloc_port;
template <int limit> volatile int HLS_malloc(int size,
volatile hidmm_alloc_port *allocator)
{
#pragma HLS INLINE
int status;
io_section_HLS_malloc:
{
#pragma HLS PROTOCOL fixed
allocator->cmd = 2; // send cmd and size to allocator
#pragma HLS PROTOCOL fixed
allocator->size = size;
allocator->free_target = 0;
ap_wait();
status = allocator->addr;
if (status >= limit)
return -1;
else
return status;
}
}
template <int unused> volatile int HLS_free(int free_target,
int free_size, volatile hidmm_alloc_port *allocator)
{
#pragma HLS INLINE
int status;
io_section_HLS_free:
{
#pragma HLS PROTOCOL fixed
allocator->cmd = 3; // send cmd and size to allocator
#pragma HLS PROTOCOL fixed
allocator->size = free_size;
allocator->free_target = free_target;
return 1;
}
}
//involved in the following operations
// allocation
size_new_node = SIZE_LIST; // HI-DMM insert: set size of pointer
// HI-DMM replace: new_node = (LIST *)malloc(sizeof(LIST));
offset_new_node = HLS_malloc<2341>(1, Hi_DMM_allocator_0_KWTA_mini16) * SIZE_LIST;
new_node = Hi_DMM_dynamic_heap_0 + offset_new_node; // HI-DMM insert: stress offset of pointer new_node
//de-allocation
// HI-DMM replace: free((unsigned long long)now);
HLS_free<0>(offset_now, size_now, Hi_DMM_allocator_0_KWTA_mini16);
heap arrays: The output of source code analysis includes the depths and types of heaps. Hi-DMM will declare them as array in the transformed source code. The allocated pointers will point to particular offsets in these heaps.
int Hi_DMM_dynamic_heap_0[16384]; //['now', 'left', 'right', 'parent', 'head', 'tail', 'last_parent', 'new_node', 'test_struct_array']---MAU_size=1---Allocator Management Capability Required: 2341
int Hi_DMM_dynamic_heap_1[8192]; //['local_dis']---MAU_size=1---Allocator Management Capability Required: 8192
//involved in the following operations
//allocation
size_new_node = SIZE_LIST; // HI-DMM insert: set size of pointer
// HI-DMM replace: new_node = (LIST *)malloc(sizeof(LIST));
offset_new_node = HLS_malloc<2341>(1, Hi_DMM_allocator_0_KWTA_mini16) * SIZE_LIST;
new_node = Hi_DMM_dynamic_heap_0 + offset_new_node; // HI-DMM insert: stress offset of pointer new_node
I/O interfaces: In order to let the accelerator communicate with allocators, Hi-DMM will insert Hi-DMM ports into the argument list (interface) and define them as handshake interface.
// HI-DMM replace: void HLS_MAXHEAP_HTA(int n, int data[20000], int dis_output[200])
void HLS_MAXHEAP_HTA(int n, int data[20000], int dis_output[200], hidmm_alloc_port *Hi_DMM_allocator_0_KWTA_mini16, hidmm_alloc_port *Hi_DMM_allocator_1_Super_HTA8k)
{
#pragma HLS interface ap_hs port = Hi_DMM_allocator_0_KWTA_mini16
#pragma HLS interface ap_hs port = Hi_DMM_allocator_1_Super_HTA8k
//............
//............
//............
}
Redundant assignment before the access to pinters: due to some unknown problems with Vivado HLS, sometime the value of a pointer (e.g. int *a) will be changed to zero (e.g. a == 0). As result, some accesses to the pointer will fail (e.g. b = a[12];). To ensure the stability of system and fix this issue, we re-assign (stress) the pointer with its offset in the heap before each access to the pointer (e.g. a=HiDMM_heap_0 + offset_a;). After Vivado HLS fixes this bug, such re-assignment operation can be removed.
new_node = Hi_DMM_dynamic_heap_0 + offset_new_node; // HI-DMM insert: stress offset of pointer new_node
// HI-DMM replace: new_node->VAL = data[i];
new_node[OFFSET_LIST_VAL] = data[i];
The function calls, like malloc() and free(), which are not available in HLS, should be replaced by Hi-DMM functions, which are connected to the allocators which are mapped to heaps in previous steps.
//allocation
// HI-DMM replace: local_dis = (int *)malloc(n * sizeof(int));
offset_local_dis = HLS_malloc<8192>(n, Hi_DMM_allocator_1_Super_HTA8k);
// HI-DMM insert: stress offset of pointer local_dis
local_dis = Hi_DMM_dynamic_heap_1 + offset_local_dis;
//de-alloction
// HI-DMM replace: free((unsigned long long)now);
HLS_free<0>(offset_now, size_now, Hi_DMM_allocator_0_KWTA_mini16);
Moreover, since Hi-DMM supports the dynamic allocation of user-defined struct ("struct" in C), the accesses of the components of these structures are also transformed into expressions based on pointers and offsets.
//access to struct pointer
// HI-DMM replace: head->VAL = data[0];
head[OFFSET_LIST_VAL] = data[0];
head = Hi_DMM_dynamic_heap_0 + offset_head; // HI-DMM insert: stress offset of pointer head
// HI-DMM replace: head->NEXT = NULL;
head[OFFSET_LIST_NEXT] = NULL;
head = Hi_DMM_dynamic_heap_0 + offset_head; // HI-DMM insert: stress offset of pointer head
// HI-DMM replace: head->PREVIOUS = NULL;
head[OFFSET_LIST_PREVIOUS] = NULL;
head = Hi_DMM_dynamic_heap_0 + offset_head; // HI-DMM insert: stress offset of pointer head
// HI-DMM replace: head->PARENT = NULL;
head[OFFSET_LIST_PARENT] = NULL;
head = Hi_DMM_dynamic_heap_0 + offset_head; // HI-DMM insert: stress offset of pointer head
// HI-DMM replace: head->LEFT = NULL;
head[OFFSET_LIST_LEFT] = NULL;
head = Hi_DMM_dynamic_heap_0 + offset_head; // HI-DMM insert: stress offset of pointer head
// HI-DMM replace: head->RIGHT = NULL;
head[OFFSET_LIST_RIGHT] = NULL;
head = Hi_DMM_dynamic_heap_0 + offset_head; // HI-DMM insert: stress offset of pointer head
// HI-DMM replace: head->IAMLEFT = -1;
head[OFFSET_LIST_IAMLEFT] = -1;