#c #fpga #vivado-hls
#c #fpga #vivado-hls
Вопрос:
Для примера у меня есть цикл, в котором содержимое массива считывается и записывается за один цикл на итерацию.
int my_array[10] = ...;
for(i=0; i<10; i ) {
if(i<5) {
my_array[i*2] = func_b(my_array[i*2]);//func_b takes double the time of func_a, but it also runs on 1/2 of the time.
}
func_a(myarray[i]);//func_a is executed quickly
}
один и тот же элемент доступен только для операций чтения / записи i=0
, с надлежащей задержкой, и алгоритм синхронизации должен допускать распараллеливание. Я изо всех сил пытаюсь найти правильные HLS-прагмы, чтобы заставить их работать параллельно, вот так
//#pragma HLS to delay by two cycles
for(i=0; i<10; i ) {
func_a(my_array[i]);
}
//#pragma HLS to allow to run each iteration in parallel with the first loop, if possible in two cycles
for(i=0; i<5; i ) {
my_array[i*2] = func_b(my_array[i]);//func_b might be split into two halves for each to fit into one cycle.
}
в идеале первый цикл должен завершать два цикла после завершения второго и таким образом считывать правильное значение последнего my_array
элемента. Возможно, у меня неправильное представление, но я надеюсь, что разделение работы таким образом (через два цикла) должно увеличить тактовую частоту?
Остальная часть сгенерированного кода Verilog в порядке, хотя он достаточно загадочный, поэтому я предпочел бы придерживаться C, чем пытаться изменить то, что было сгенерировано HDL. Любые предложения о том, как распараллелить его или если это выполнимо?
Ответ №1:
В качестве первого замечания, тактовая частота не обязательно зависит от того, выполняются циклы параллельно или нет. Что может улучшить, так это латентность алгоритма, то есть общее количество циклов, необходимых для его запуска.
Существуют элементы, обрабатываемые обеими функциями в одном цикле. Поэтому, если вы не перепишете функции (возможно, объединив их вместе), у вас есть зависимость от данных, которую вы не можете разорвать (или ваш алгоритм просто не сработает). Проверьте индексы, к которым вы обращаетесь, и проверьте, остается ли порядок операций неизменным:
for(int i = 0; i < 10; i ) {
if (i < 5) {
// Accessing indexes: 0, 2, 4, 6, 8
my_array[i * 2] = func_b(my_array[i * 2]);
}
// Accessing indexes: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
func_a(myarray[i]);
}
Предложенное вами решение, второй фрагмент кода, не будет работать, потому что не выполняет то же самое в том же порядке, что и в первом фрагменте кода (это должно быть допустимо, если вы просто запускаете код на C / C , который должен иметь тот же результат).
Тем не менее, одна вещь, которую вы можете попробовать, это поместить прагму ПОТОКА ДАННЫХ следующим образом:
#pragma HLS DATAFLOW
for(i = 0; i < 5; i ) {
my_array[i * 2] = func_b(my_array[i * 2]);
}
for(i = 0; i < 10; i ) {
func_a(my_array[i]);
}
Но я не уверен, будет ли HLS правильно обрабатывать my_array
буфер, поскольку он будет использоваться обоими циклами / процессами.
Одна из последних идей, которую я должен полностью разрешить ПОТОКУ ДАННЫХ, т. Е. Путем создания алгоритма производитель-потребитель, состоит в том, чтобы разделить my_array
на два разных буфера, по одному для каждой части алгоритма ( i
четного или нечетного), например:
int my_array_evens[5] = ...;
int my_array_odds[5] = ...;
#pragma HLS DATAFLOW
for(i = 0; i < 5; i ) {
my_array_evens[i * 2] = func_b(my_array_evens[i * 2]); // Dataflow producer
}
for(i = 0; i < 10; i ) {
if (i % 2 == 0) {
func_a(my_array_evens[i]); // Dataflow consumer
} else {
func_a(my_array_odds[i]);
}
}
ПРИМЕЧАНИЕ: Если функции относительно небольшие, добавьте в циклы КОНВЕЙЕРНЫЕ прагмы, чтобы «ускорить» процесс.