2013-02-25 7 views
3

Я пытаюсь создать синтезируемый, параметризованный кодер приоритета в Verilog. В частности, я хочу, чтобы найти значащий 1 в векторе и возвращает вектор, содержащий только что 1. Например:Сгенерировать, если выписки в Verilog

IN[3:0] | OUT[4:0] 
--------+--------- 
1010 | 00010 
1111 | 00001 
0100 | 00100 
0000 | 10000 (special case) 

Таким образом, если векторы четыре бита, код:

if (in[0]==1'b1) least_one = 1; 
else if (in[1]==1'b1) least_one = 2; 
else if (in[2]==1'b1) least_one = 4; 
else if (in[3]==1'b1) least_one = 8; 
else out = 16; // special case in==0, set carry bit 

Мне нужен общий, масштабируемый способ сделать это, потому что длина вектора ввода/вывода параметризована. Мой текущий код:

module least_one_onehot 
#(parameter ADDR_WIDTH=4) 
(output reg [ADDR_WIDTH:0] least_one, 
input [ADDR_WIDTH-1:0] in); 

genvar i; 

always @(in) begin 
    if (in[0]==1'b1) least_one = 1; 
    generate for (i=1; i<ADDR_WIDTH; i=i+1) begin : U 
     else if (in[i]==1'b1) least_one = 2**i; 
     end 
     endgenerate 
    else least_one = 2**ADDR_WIDTH; 
    end 

endmodule 

Когда я пытаюсь скомпилировать это я получаю следующие ошибки:

file: least_one_onehot.v 
     generate for (i=1; i<ADDR_WIDTH; i=i+1) begin : U 
       | 
ncvlog: *E,GIWSCP (least_one_onehot.v,10|8): Generated instantiation can only be valid within a module scope [12.1.3(IEEE 2001)]. 
         else if (in[i]==1'b1) least_one = 2**i; 
          | 
ncvlog: *E,NOTSTT (least_one_onehot.v,11|6): expecting a statement [9(IEEE)]. 
       endgenerate 
          | 
ncvlog: *E,GIWSCP (least_one_onehot.v,13|12): Generated instantiation can only be valid within a module scope [12.1.3(IEEE 2001)]. 
       else least_one = 2**ADDR_WIDTH; 
        | 
ncvlog: *E,NOTSTT (least_one_onehot.v,14|5): expecting a statement [9(IEEE)] 

Я пробовал различные варианты расположения генерации, если, и всегда заявления, все без успех. Кто-нибудь знает правильный синтаксис для этого? Реализация заявлений или других альтернатив также будет прекрасной. Благодарю.

+0

Должно ли ваша ширина выходного сигнала соответствовать вашей ширине ввода? Что такое 'out'? – toolic

+0

Существует разница между процедурными контекстами и контекстами модулей. См. [This] (http://stackoverflow.com/questions/10443368/unknown-verilog-error-expecting-endmodule/10449997#10449997) ответ. – 2013-02-26 03:34:29

+0

Вывод должен быть в 1 бит шире, чем в случае необходимости для всего нуля. Я уточнил оригинальный пост. – DrDean

ответ

2

Я думаю, вы неправильно понимаете, как работает генерация. Это не текстовый препроцессор, который испускает код между парой generate/endgenerate с соответствующими подстановками. У вас должны быть синтаксические сущности, имеющие пару. У меня нет доступа к тренажере прямо в эту минуту, но это может сделать трюк для вас (совершенно непроверенные)

genvar i; 
generate 
    for (i = 1; i < ADDR_WIDTH; i = i + 1) begin : U 
     least_one[i] = in[i] & ~|in[i - 1:0]; 
    end 
endgenerate 
least_one[0] = in[0]; 
least_one[ADDR_WIDTH] = ~|in; 

Обычно Verilog будет жаловаться на непостоянной ширины немного среза, но так как это в пределах генерировать цикл это может сработать.

Если что-то подобное выше, вы просто проверяете первый бит набора в цикле и затем декодируете этот результат.

+1

Вам нужно префикс 'присваивать 'всем операторам' наименьшее [N] = '. В противном случае ваш код будет выглядеть правильно. – Greg

+0

@Brian Magnuson: Неэффективность этого кода (что становится очевидным, когда «ADDR_WIDTH» становится большим) заключается в том, что его размер равен O (N log N), где N = 'ADDR_WIDTH'. Вычисление «наименьшая» может быть легко сформулировано как [параллельное префиксное вычисление] (http: //xlinux.nist.gov/dads/HTML/parallelPrefix.html), который делает размер дизайна O (N). В основном, он использует тот факт, что термины '| in [i-1: 0]' имеют много общей логики. –

+0

Когда я сталкиваюсь с такой проблемой (особенно когда ширина бит велика), когда трудно записать логику вычисления параллельного префикса в выражении 'generate', я использую (параметризованный) скрипт Perl для генерации кода SystemVerilog и вызывать его в Makefile перед компиляцией для моделирования или синтеза. –

1

Вы делаете не нужен блок генерации. Вы можете использовать:

integer i; 
reg found; 
always @(in) begin 
    least_one = {(ADDR_WIDTH+1){1'b0}}; 
    found = 1'b0; 
    for (i=0; i<ADDR_WIDTH; i=i+1) begin 
    if (in[i]==1'b1 && found==1'b0) begin 
     least_one[i] = 1'b1; 
     found = 1'b1; 
    end 
    end 
    least_one[ADDR_WIDTH] = (found==1'b0); 
end 

Если вы действительно хотите использовать генерирующий блок, вам необходимо назначить каждый бит.

assign least_one[0] = in[0]; 
assign least_one[ADDR_WIDTH] = (in == {ADDR_WIDTH{1'b0}}); 
genvar i; 
generate 
    for (i=1; i<ADDR_WIDTH; i=i+1) begin : U 
    assign least_one[i] = in[i] && (in[i - 1:0] == {i{1'b0}}); 
    end 
endgenerate 
+0

Правда, мне не требуется инструкция generate. Любое синтезируемое, которое выполняет свою работу, прекрасно. – DrDean

1

Лично мне нравится следующий блок кода для того, что вам нужно: назначить из = {1'b1, в} & ((~ {1'b1, в}) + 1);

Вы можете попробовать это (сбросив лишний бит для удобочитаемости), но мне нравится явно делать комплимент twos, чтобы избежать возможных проблем с совместимостью.

assign out = in & (-1 * in);

Смежные вопросы