Казалось бы простая задача: как развернуть биты в шине так, чтоб младший бит стал старшим, а старший самым младшим? Первое, что приходит на ум: написать вот так:
reg [7:0]src;
//reverse?
wire [0:7]re1; assign re1 = src; //does not work..
Но это так не работает!
Какие есть работающие варианты?
Первый и самый понятный способ - это расписывать каждый бит в отдельности. Вот так:
//method 1
wire [7:0]re2; assign re2 = {src[0],src[1],src[2],src[3],src[4],src[5],src[6],src[7]};
Это отличный метод, который использует оператор конкатенации, объединяет известные сигналы в новом нужном порядке. Этим методом легко пользоваться, когда число сигналов в шине не велико. Если же шина многоразрядная, 64, 128, 1024 бита, то как быть?
Второй метод - это написать и использовать специальную функцию:
function [7:0] fbit_reverse ( input [7:0] data );
integer i;
begin
for ( i=0; i<8; i=i+1 )
begin
fbit_reverse[7-i] = data[i];
end
end
endfunction
В эту функцию можно передать восьмибитный сигнал и она выдаст нам другой восьмибитный сигнал, где биты уже развернуты, как нужно. Вот так:
//method 2, use reverse function
wire [7:0]re3; assign re3 = fbit_reverse(src);
Хорошее решение, но не придумал, как сделать его параметризуемым. Что если есть несколько шин которые имеют разную разрядность и каждую нужно развернуть? Тогда можно сделать отдельный параметризуемый модуль. Например, вот так:
module mod_bit_reverse( in, out );
parameter NUM_BITS = 16;
input wire [NUM_BITS-1:0]in;
output wire [NUM_BITS-1:0]out;
genvar i;
generate
for(i=0; i<NUM_BITS; i=i+1)
begin
assign out[NUM_BITS-1-i] = in[i];
end
endgenerate
endmodule
Этот модуль можно вставлять в другие модули и там и сям и задавать разрядность шин в параметре.
Вставить модуль в другой модуль верилом можно так:
wire [7:0]re4;
mod_bit_reverse // module name
#(.NUM_BITS(8)) //module parameter, bus width
my_reverse_module //module instance name
(
.in(src), // module signals
.out(re4)
);
Есть еще один вариант - использовать SystemVerilog Streaming Operator.
wire [7:0]re5;
assign re5 = {<<{src}};
Это замечательно работает, если симулировать в Modelsim. Для симуляции подходит даже Modelsim Starter Edition Intel v10.5b, который идет в комплекте с 16м квартусом.
Однако, похоже, что сам квартус такое не поддерживает. Увы.
Симулятор icarus verilog так же на эту строку дает ошибку - что-то вроде "sorry, streaming operators are not supported".
Чтобы не быть голословным, объединяю все описанные выше методы в один Verilog Testbench файл и пробую симулировать в Modelsim.
Вот тестбенч:
`timescale 1ns/1ns
module tb();
//source bits which should be reversed
reg [7:0]src;
//this method does not work! no reverse bits!!
wire [0:7]re1; assign re1 = src;
//method 1
wire [7:0]re2; assign re2 = {src[0],src[1],src[2],src[3],src[4],src[5],src[6],src[7]};
//method 2, use reverse function
wire [7:0]re3; assign re3 = fbit_reverse(src);
//method 3, use reversing module
wire [7:0]re4;
mod_bit_reverse #(.NUM_BITS(8)) my_reverse_module( .in(src), .out(re4) );
//method 4
//SystemVerilog streaming operators do not work neither with icarus verilog nor with Quartus Prime
//Modelsim Starter Edition v10.5b supports this
wire [7:0]re5;
assign re5 = {<<{src}};
initial
begin
$dumpfile("out.vcd");
$dumpvars(0,tb);
#10;
src = 8'hA1; #1;
$display("source %X",src);
$display("try1 reverse %X",re1);
$display("try2 reverse %X",re2);
$display("try3 reverse %X",re3);
$display("try4 reverse %X",re4);
$display("try5 reverse %X",re5);
#10;
src = 8'h78; #1;
$display("source %X",src);
$display("try1 reverse %X",re1);
$display("try2 reverse %X",re2);
$display("try3 reverse %X",re3);
$display("try4 reverse %X",re4);
$display("try5 reverse %X",re5);
#10;
src = 8'h1C; #1;
$display("source %X",src);
$display("try1 reverse %X",re1);
$display("try2 reverse %X",re2);
$display("try3 reverse %X",re3);
$display("try4 reverse %X",re4);
$display("try5 reverse %X",re5);
#10;
#10;
$finish();
end
function [7:0] fbit_reverse ( input [7:0] data );
integer i;
begin
for ( i=0; i<8; i=i+1 )
begin
fbit_reverse[7-i] = data[i];
end
end
endfunction
endmodule
module mod_bit_reverse( in, out );
parameter NUM_BITS = 16;
input wire [NUM_BITS-1:0]in;
output wire [NUM_BITS-1:0]out;
genvar i;
generate
for(i=0; i<NUM_BITS; i=i+1)
begin
assign out[NUM_BITS-1-i] = in[i];
end
endgenerate
endmodule
Еще напишу вспомогательный файл TB.DO со списоком команд симулятора Modelsim:
vlib work1
vlog -work work1 tb.v
vsim work1.tb
run -all
Запускаю в командной строке Modelsim:
>vsim -c -do tb.do
В консоли видно, как верилоговская системная функция $display выводит развернутый байт для методов 2, 3, 4 и 5. Метод 1, как я и говорил - не работает.
Кроме того, тестбенч создает файл OUT.VCD, который содержит информацию о сигналах проекта и который можно посмотреть с помощью gtkwave. Вот эти сигналы (скриншот программы gtkwave):
Ну и напоследок, пожалуй нужно заметить, что любая логика разворачивающая шину, даже модуль mod_bit_reverse, в микросхеме FPGA абсолютно не занимает никакого места. И это правильно, ведь фактически, никакой логики там и нет - есть просто переименование сигналов.
Подробнее...