Преобразование кода Грея в двоичное число

gray code wheel

Как мы знаем, коды Грея (Gray codes) – это специальная система счисления, в которой два соседних значения отличаются только в одном разряде. Они часто используются для повышения надежности аппаратуры. Одно из применений кодов Грея – передача значений указателей головы/хвоста очереди из одного клокового домена в другой при проектировании асинхронного FIFO.

Рассмотрим способы преобразования кодов Грея в двоичное число.
Вот таблица соответствия для четырехбитных чисел:

0000    0000
0001    0001
0010    0011
0011    0010
0100    0110
0101    0111
0110    0101
0111    0100
1000    1100
1001    1101
1010    1111
1011    1110
1100    1010
1101    1011
1110    1001
1111    1000

Столбец слева – это двоичное число, а столбец справа – это соответствующие коды Грея.
Преобразование из Грея в двоичное число можно сделать на языке Verilog HDL вот так:


module gray2bin_v1(
   input  wire [3:0]gray,
   output wire [3:0]bin
);

assign bin[0] = gray[3] ^ gray[2] ^ gray[1] ^ gray[0];
assign bin[1] = gray[3] ^ gray[2] ^ gray[1];
assign bin[2] = gray[3] ^ gray[2];
assign bin[3] = gray[3];

endmodule

Используются только операции «Исключающее ИЛИ». Если откомпилировать такой модуль с помощью Quartus II, то в его RTLViewer можно увидеть получившуюся эквивалентную схему:

Преобразование кода Грея в двоичный код, схема в Altera RTLViewer

Для проверки правильности работы этого модуля напишем Verilog тестбенч:
`timescale 1ns/1ns

module test();

reg clk;
initial clk=0;
always
   #10 clk= ~clk;

reg  [3:0]gr;
wire [3:0]b1;

gray2bin_v1 my_gr1(
    .gray(gr),
    .bin(b1)
    );

initial
begin

$dumpfile("out.vcd");
$dumpvars(-1, test);

gr=4'b0000;
@(posedge clk); #0;
@(posedge clk); #0;
gr=4'b0001;
@(posedge clk); #0;
gr=4'b0011;
@(posedge clk); #0;
gr=4'b0010;
@(posedge clk); #0;
gr=4'b0110;
@(posedge clk); #0;
gr=4'b0111;
@(posedge clk); #0;
gr=4'b0101;
@(posedge clk); #0;
gr=4'b0100;
@(posedge clk); #0;
gr=4'b1100;
@(posedge clk); #0;
gr=4'b1101;
@(posedge clk); #0;
gr=4'b1111;
@(posedge clk); #0;
gr=4'b1110;
@(posedge clk); #0;
gr=4'b1010;
@(posedge clk); #0;
gr=4'b1011;
@(posedge clk); #0;
gr=4'b1001;
@(posedge clk); #0;
gr=4'b1000;
@(posedge clk); #0;

$finish();
end
endmodule

Быстро просимулировать и посмотреть результат можно с помощью iverilog (Icarus Verilog), используя командную строку и GtkWave. Здесь же я приведу лишь получившуюся временную диаграмму:

Временная диаграмма сигналов преобразователя кодов Грея в двоичные числа

Все работает правильно.
Недостаток описанного выше примера модуля gray2bin_v1 состоит в том, что он реализует фиксированное число бит, в данном случае четыре. Было бы удобным иметь параметризованный модуль, который без изменений мог бы быть использованным в разных  проектах.

Если внимательно посмотреть на код модуля gray2bin_v1, то видно, что там имеется четыре строки assign для каждого результирующего бита. Нам нужен механизм создания заданного числа таких строк кода Verilog в момент компиляции и такой механизм есть в стандарте Verilog-2001. Использование конструкции языка generate/endgenerate позволяет в момент компиляции генерировать фрагменты кода.

Вот пример параметризованного модуля преобразователя кода Грея в двоичный код:
module gray2bin_v2(
   input  wire [SIZE-1:0]gray,
   output wire [SIZE-1:0]bin
   );
parameter SIZE = 4;

genvar i;

generate
 for
(i=0; i<SIZE; i=i+1)
 begin: bit
   assign bin[i] = ^gray[SIZE-1:i];
 end
endgenerate
endmodule

Переменная типа genvar существует только в момент компиляции и генерации кода. Во время симуляции ее нет. Строка “assign…” будет сгенерирована SIZE раз. Для каждого бита результирующего двоичного числа операция «Исключающее ИЛИ» будет проводиться по диапазону бит исходного кода Грея: ^gray[SIZE-1:i].

Конечно, можно написать модуль gray2bin и иначе, без «экзотических» generate/endgenerate. Вот так:
module gray2bin_v3(
input wire [SIZE-1:0]gray,
output reg [SIZE-1:0]bin
);
parameter SIZE = 4;

integer i;

always @*
 begin
  for
(i=0; i<SIZE; i=i+1)
    bin[i] = ^(gray>>i);
 end
endmodule

Здесь, выражение (gray>>i) – это логический сдвиг вправо, он оставляет только нужные биты, между которыми будет проводиться «Исключающее ИЛИ». К сожалению, написать здеь gray[SIZE-1:i] вместо (gray>>i) не получится. Компилятор Verilog выдаст ошибку. Язык Verilog не позволяет выбрать часть бит из шины если один из индексов это переменная.

Модули gray2bin_v2 и gray2bin_v3 описывают одно и то же, разными способами. Они работают одинаково. В этом легко убедиться с помощью того же тестбенча, описанного выше.

 


Добавить комментарий