МАРСОХОД

Open Source Hardware Project

Язык описания аппаратуры Verilog HDL

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

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 описывают одно и то же, разными способами. Они работают одинаково. В этом легко убедиться с помощью того же тестбенча, описанного выше.

 

Комментарии  

0 #4 leonem 11.03.2014 10:07
Хотел узнать про generate написаный мной код не работает а в чем проблема не понятно очень хотелось узнать как переделать.
assign sr_out не заполняется результатами или где то у меня ещё ошибка.
input wire [7:0]sr_in;
output [7:0]sr_out,sr_out1,sr_out2,sr_out3,sr_out4;
reg [31:0]sr;
always @ (posedge clk)
if (shift ==1 )
sr
0 #3 leonem 11.03.2014 09:58
Не получается вставить весь код
0 #2 picachu 31.05.2013 07:14
Не вполне понял про преобразование "бинарного" значения в код Грея (вроде как нету его тут совсем), на всякий случай добавлю, вот такой код сработал у меня:

module bin2gray_v1(
input wire [SIZE-1:0]bin,
output reg [SIZE-1:0]gray
);
parameter SIZE = 8;

always @*
gray = (bin>>1) ^ bin;

endmodule

Сначала преобразовываю в код Грея, потом обратно в бинарный.
0 #1 SiriZen 21.01.2013 15:34
Сразу хочется поблагодарить за Ваш труд, очень нужный и полезный!

А за одно попросить, если это возможно, дать подробное построчное описание для тест-бенча, или вообще посвятить один урок тест-бенчам, думаю это поможет многим!

Спасибо!

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


Защитный код
Обновить


GitHub YouTube Twitter
Вы здесь: Начало Verilog Преобразование кода Грея в двоичное число