Перейти к содержанию
    

Автомат универсального регистра

Добрый день. Пытаюсь собрать автомат универсального сдвигового регистра на Verilog. Получился вот такой код. Iverilog синтезирует и тестбенч работает корректно, но Quartus ругается на использование переменной Q в двух блоках always. Пробовал ввести дополнительную переменную вместо Q во втором блоке always (для последовательного вывода) и присвоить Q в блоке "логика автомата" ее значение, но, к сожалению, таким образом последовательный вывод перестает работать. Также попробовал еще несколько различных вариантов, но результат все тот же. Подскажите, пожалуйста, в какую сторону смотреть. Спасибо. 

module Universal_Shift_Register #(parameter WIDTH = 5) // параметр позволяет быстро менять разрядность входного числа
                                (clk, res, D, out_state, Load, M, enable);

localparam parall_out = 1, logic_shft_left = 2, logic_shft_right = 3, ring_shft_left = 4, ring_shft_right = 5, serial_out  = 6;

input [WIDTH:0] D; //Входное число
input     [2:0] Load; //Выбор действия
input       res, clk;
input    [WIDTH:0] M; //Количество сдвигаемых разрядов
input enable;

reg [WIDTH:0] Q; //Результат 
reg [2:0] state, next_state;

output [WIDTH:0] out_state; 

always @(posedge clk, negedge res) 
begin
  if (!res)
    state <= parall_out;
  else
    state <= next_state;
end

always @(*) //Логика автомата
    case (state)
        parall_out:
    		begin
    		    if(Load == 1)
                    begin
						Q = D; //Параллельный вывод
                        next_state = logic_shft_left;
    				end
				else next_state = parall_out;
    		end
        logic_shft_left:
    		begin
    		    if(Load == 2)
                    begin
                        Q = D << M; //Логический сдвиг влево на М разрадов
                        next_state = logic_shft_right;
    				end
				else next_state = logic_shft_left;
    		end
    	logic_shft_right:
    		begin
    		    if(Load == 3)
    				begin
    	                Q = D >> M; //Логический сдвиг вправо на М разрадов
                        next_state = ring_shft_left;
    			    end
				else next_state = logic_shft_right;
    		end 
        ring_shft_left:
    		begin
    		    if(Load == 4)
                    begin
                        Q = {D[WIDTH-2:0], D[WIDTH-1]}; // Кольцевой сдвиг влево 
                        next_state = ring_shft_right;
    				end
				else next_state = ring_shft_left;
    		end
    	ring_shft_right:
    		begin
    		    if(Load == 5)
    				begin
    	                Q = {D[0], D[WIDTH-1:1]}; // Кольцевой сдвиг вправо 
                        next_state = serial_out;
    			    end
				else next_state = ring_shft_right;
    		end    
		serial_out:
    		begin
    		    if(Load == 6) // Последовательный вывод
    				begin
                        next_state = parall_out;
    			    end
				else next_state = serial_out;
    		end
		default : next_state = parall_out;
	endcase

reg [WIDTH:0]qQ;

// Логика для последовательного вывода
always @ (posedge clk, negedge res) // По приходу тактового импульса, в выходную переменную будет записываться [0] разряд входного числа
	if (!res)
    	Q <= 0;
    else if (Load == 6 && enable == 1) 	// enable служит для разрешения записи входного числа в промежуточную REG переменную qQ
		qQ <= D;  
	else if (Load == 6 && enable == 0) 
		begin
     		Q <= qQ[0]; 				// Выводим младший разряд входного числа 
			qQ <= {qQ[0], qQ[WIDTH:1]}; // Сдвигаем входное число вправо для передачи следующего разряда
		end

// Логика для реализации передачи результата
assign out_state = Q;

endmodule

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

А смотреть как обычно, на бумажке разрисовать функциональную схему модуля, расставить триггеры, логику и описать. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, SarBon88 сказал:

Подскажите, пожалуйста, в какую сторону смотреть

можно начать c описывания и проверки упрощенной версии Вашего модуля

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

always @(*) //Логика автомата
    case (Load)
        parall_out:
            Q = D; //Параллельный вывод
        logic_shft_left:
            Q = D << M; //Логический сдвиг влево на М разрадов
    	logic_shft_right:
            Q = D >> M; //Логический сдвиг вправо на М разрадов
        ring_shft_left:
            Q = {D[WIDTH-1:0], D[WIDTH]}; // Кольцевой сдвиг влево 
    	ring_shft_right:
            Q = {D[0], D[WIDTH:1]}; // Кольцевой сдвиг вправо 
		serial_out:
            Q = qQ[0];// Выводим младший разряд входного числа 
		default: 
            Q = D; //Параллельный вывод
	endcase

и не придётся использовать выход Q в описании работы с промежуточной переменной qQ при последовательном выводе

// Логика для последовательного вывода
always @ (posedge clk, negedge res) // По приходу тактового импульса, в выходную переменную будет записываться [0] разряд входного числа
	if (!res)
    	qQ <= 0;
    else if ((Load == serial_out) && (enable == 1)) 	// enable служит для разрешения записи входного числа в промежуточную REG переменную qQ
		qQ <= D;  
	else if ((Load == serial_out) && (enable == 0)) 
		begin
			qQ <= {qQ[0], qQ[WIDTH:1]}; // Сдвигаем входное число вправо для передачи следующего разряда
		end

 

весь модуль будет выглядеть так

module Universal_Shift_Register #(parameter WIDTH = 5) // параметр позволяет быстро менять разрядность входного числа
                                (clk, res, D, Load, M, enable, out_state);

localparam parall_out = 1, logic_shft_left = 2, logic_shft_right = 3, ring_shft_left = 4, ring_shft_right = 5, serial_out  = 6;

input       res, clk;

input [WIDTH:0] D;      //Входное число
input     [2:0] Load;   //Выбор действия
input [WIDTH:0] M;      //Количество сдвигаемых разрядов
input enable;

output [WIDTH:0] out_state; 

reg [WIDTH:0] Q; //Результат 
reg [WIDTH:0]qQ; //Промежуточная переменная

always @(*) //Логика автомата
    case (Load)
        parall_out:
            Q = D; //Параллельный вывод
        logic_shft_left:
            Q = D << M; //Логический сдвиг влево на М разрадов
    	logic_shft_right:
            Q = D >> M; //Логический сдвиг вправо на М разрадов
        ring_shft_left:
            Q = {D[WIDTH-1:0], D[WIDTH]}; // Кольцевой сдвиг влево 
    	ring_shft_right:
            Q = {D[0], D[WIDTH:1]}; // Кольцевой сдвиг вправо 
		serial_out:
            Q = qQ[0];// Выводим младший разряд входного числа 
		default: 
            Q = D; //Параллельный вывод
	endcase


// Логика для последовательного вывода
always @ (posedge clk, negedge res) // По приходу тактового импульса, в выходную переменную будет записываться [0] разряд входного числа
	if (!res)
    	qQ <= 0;
    else if ((Load == serial_out) && (enable == 1)) 	// enable служит для разрешения записи входного числа в промежуточную REG переменную qQ
		qQ <= D;  
	else if ((Load == serial_out) && (enable == 0)) 
		begin
			qQ <= {qQ[0], qQ[WIDTH:1]}; // Сдвигаем входное число вправо для передачи следующего разряда
		end

// Логика для реализации передачи результата
assign out_state = Q;

endmodule

такая упрощенная версия модуля работает корректно  во всех шести режимах

image.thumb.png.2060d72f5109540ab5e4c43b6f4e2d0c.png

для проверки использовался такой тестбенч:

Спойлер
module Universal_Shift_Register_tb();

reg clk = 0;

always#(5) clk = ~clk;

reg res = 0;


localparam WIDTH = 5;
localparam parall_out = 1, logic_shft_left = 2, logic_shft_right = 3, ring_shft_left = 4, ring_shft_right = 5, serial_out  = 6;

reg [WIDTH:0] D     = 0;   //Входное число
reg     [2:0] Load  = 0;   //Выбор действия
reg [WIDTH:0] M     = 0;   //Количество сдвигаемых разрядов
reg enable          = 0;

wire [WIDTH:0] out_state;

initial
begin
    #21;
    res = 1;
    
    // проверяем parall_out
    @(negedge clk);
    Load = parall_out;
    D = 59;
    @(negedge clk);
    D = 58;
    @(negedge clk);
    D = 57;
    
    // проверяем logic_shft_left
    @(negedge clk);
    Load = logic_shft_left;
    M = 0;
    @(negedge clk);
    M = 1;
    @(negedge clk);
    M = 2;
    
    // проверяем logic_shft_right
    @(negedge clk);
    Load = logic_shft_right;
    M = 0;
    @(negedge clk);
    M = 1;
    @(negedge clk);
    M = 2;
    
    // проверяем ring_shft_left
    @(negedge clk);
    Load = ring_shft_left;
    D = 59;
    @(negedge clk);
    D = 58;
    @(negedge clk);
    D = 57;
    
    // проверяем ring_shft_right
    @(negedge clk);
    Load = ring_shft_right;
    D = 59;
    @(negedge clk);
    D = 58;
    @(negedge clk);
    D = 57;
    
    // проверяем ring_shft_right
    @(negedge clk);
    Load = serial_out;
    enable = 1;
    @(negedge clk);
    enable = 0;
    
end

Universal_Shift_Register #(WIDTH) Universal_Shift_Register_i (clk, res, D, Load, M, enable, out_state);

endmodule

 

так как тут нет конфликта с выходом Q, то такая упрощенная версия модуля синтезируется

image.thumb.png.37eae1cc1ddb907be3c6c41bc67af066.png

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 часа назад, stealthisname сказал:

такая упрощенная версия модуля работает корректно  во всех шести режимах

Добрый день! Спасибо за столь информативный ответ! Да, безусловно, так работать будет, но все же, как я понимаю, это не автомат. Собственно пробовал точно также, как у вас в примере:

6 часов назад, stealthisname сказал:
serial_out:
	Q = qQ[0];// Выводим младший разряд входного числа 

 только в представлении автомата, однако, в состоянии "serial out" вывод был просто 0 и автомат переходил в следующее состояние. Заменил небольшой кусочек в своем коде на этот:

serial_out:
	begin
		if(Load == 6) // Последовательный вывод
    		begin
				next_state = serial_out;
				Q = qQ[0];
    		end
		else next_state = parall_out;
    end

И, вроде бы, сейчас все работает адекватно. Еще раз спасибо. Предстоит еще многому учится.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Попробуйте сделать по другому. Автомат, как таковой тут и не нужен...

Вам для работы сдвигового регистра нужно сделать набор входов управления, разрешающих делать загрузку, сдвиг, и пр. Для каждого действия - свой вход разрешения... опишите это как регистр, толькл if-else будет больше. 

и далее вставите как инстанс в проект. А в проекте в нужное время будете подавать нужный сигнал разрешения.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...