В качестве очередной простой задачки возьмем сортировку чисел. Например, формирование топ-списка(по убыванию) из потока. Нуль будем использовать для очистки топ-списка вытеснением первого из списка. Алгоритм - "лобовой" (первый пришедший в голову), на Си:
#define M 8
int a[M];
extern int d;
sort(){
int cmp[M],pos[M];
if(d==0){
for(int i=0;i<M-1;i++) a[i]=a[i+1];
a[M-1]=d;
}
else{
for(int i=0;i<M;i++) cmp[i]=d>a[i];
pos[0]=cmp[0];
for(int i=1;i<M;i++) pos[i]=cmp[i-1]^cmp[i];
for(int i=M-2;i>=0;i--) if(cmp[i]) a[i+1]=a[i];
for(int i=0;i<M;i++) if(pos[i]) a[i]=d;
}
}
Можно откомпилировать, и посмотреть, как работает:
int d;
main(){
while(1){
for(int i=0;i<M;i++) printf(" %d ",a[i]);
printf("> ");
scanf("%d",&d);
sort();
}
}
Попробуем для синтеза переписать sort() на Verilog. А лучше - на SystemVerilog. Даже если не знаете SV, и пишете все на Verilog, все-равно советую поставить в Квартусе галочку "использовать SV" - хотя-бы ради расширенной поддержки многомерных упакованных массивов.
Для примера выше, для "int a[M]" можно объявить " a[M][4][8]" - массив из М 4х-байтовых слов.
a[0] будет адресовать первое 32-разрядное слово в массиве,
a[0][1] - 2-ой байт первого слова в массиве,
a[0][1][2] - 3-ий бит 2-ого байта первого слова в массиве. Удобно!
На байты поделим, чтобы потом проще было дописать обмен по UART, добавим еще сигнал разрешения клока "en".
Прикинемся чайником, и перепишем "в лоб", методом copy/paste:
module sort #(
M=8,N=4
)(
output [M-1:0][N-1:0][8-1:0] a,
input [N-1:0][8-1:0] d,
input en,clk
);
bit [M-1:0] cmp,pos;
always@(posedge clk)
if(en)
if(d==0) begin
for(int i=0;i<M-1;i++) a[i]=a[i+1];
a[M-1]=d;
end
else begin
for(int i=0;i<M;i++) cmp[i]=d>a[i];
pos[0]=cmp[0];
for(int i=1;i<M;i++) pos[i]=cmp[i-1]^cmp[i];
for(int i=M-2;i>=0;i--) if(cmp[i]) a[i+1]=a[i];
for(int i=0;i<M;i++) if(pos[i]) a[i]=d;
end
endmodule
Заработает, как ни странно, но делать так нельзя - нарушены сразу несколько правил безопасного кодинга на Верилоге.
Правильнее будет так:
module sort #(
M=8,N=4
)(
output [M-1:0][N-1:0][8-1:0] a,
input [N-1:0][8-1:0] d,
input en,clk
);
bit [M-1:0] cmp,pos;
always_comb //или always@*
begin
for(int i=0;i<M;i++) cmp[i]=d>a[i];
pos[0]=cmp[0];
for(int i=1;i<M;i++) pos[i]=cmp[i-1]^cmp[i];
end
always_ff@(posedge clk) //или always@(posedge clk)
if(en)
if(d==0) begin
for(int i=0;i<M-1;i++) a[i]<=a[i+1];
a[M-1]<=d;
end
else begin
for(int i=M-2;i>=0;i--) if(cmp[i]) a[i+1]<=a[i];
for(int i=0;i<M;i++) if(pos[i]) a[i]<=d;
end
endmodule
Те регистры - отдельно, "провода" - отдельно.
Для "проводов" - блокирующие присваивания, для регистров - неблокирующие.
А если не заморачиваться, и синтезировать сразу из Си, прогоняя через компилятор в Верилог?
Для схемы важно различать регистры/"провода", поэтому по-любому придется вводить какие-либо новые типы(или конструкции). В своем проекте поступил так: от SV взял новые типы, причем "reg" и "bit" - без изменений, "parameter" сократил до "par", а вместо "input" - "ext", сокращенное от "extern"(Си). Вот что получилось:
par M=8,N=4;
reg [M][N][8] a;
ext [N][8] d;
ext [1] en;
sort(){
bit [M] cmp,pos;
if(en)
if(d==0){
for(int i=0;i<M-1;i++) a[i]=a[i+1];
a[M-1]=d;
}
else{
for(int i=0;i<M;i++) cmp[i]=d>a[i];
pos[0]=cmp[0];
for(int i=1;i<M;i++) pos[i]=cmp[i-1]^cmp[i];
for(int i=M-2;i>=0;i--) if(cmp[i]) a[i+1]=a[i];
for(int i=0;i<M;i++) if(pos[i]) a[i]=d;
}
}
Синтезируется, top.map.summary:
Family : Cyclone III
Total logic elements : 532
Total combinational functions : 532
Dedicated logic registers : 256
Total registers : 256
Total pins : 290
И даже работает. За такт выполняется весь код сравнения и вставки.