Verilog: если заявление неожиданное поведение
Я реализую путь данных MIPS (поведенческий) в Verilog, и когда я имитирую свой код, поведение является неожиданным. Здесь показан случай инструкций BEQ и BNE (ветвь, если равно / не равно). ALU_Out определяет, равны ли два регистра или нет. Ни один из них действительно не имеет значения, потому что моя проблема состоит в том, как воистину пропустить мое состояние. Итак, я из сигналов в форме волны, ALU_out равен нулю, но все, что происходит внутри if (ALU_Out == 32'd1), выполняется один раз. такой, что мой компьютер становится ПК +6. теперь, если мой ALU_Out на самом деле равен 1, ПК становится ПК +6+6 (Это 1 ==> становится 13). Я также включил в это выражение несколько флагов кинны и убедился, что он выполняется один раз. Даже когда я добавил еще к этому if, мои флаги ясно указывали, что if был выполнен один раз прежде. То же самое происходит в случае BNE, и желаемое условие проверяется после выполнения оператора if один раз. Не могли бы вы сказать мне, что не так с этим кодом. Спасибо заранее.
if (ALU_op == 6'd30) //BEQ
begin
ALU_out = ((ALU_in1) == (ALU_in2));
if (ALU_out == 32'd1)
begin
PC = PC + Imm_32;
end
end
if (ALU_op == 6'd31) //BNE
begin
ALU_out = ((ALU_in1) == (ALU_in2));
if (ALU_out == 0)
begin
PC = PC + Imm_32;
end
end
ОБНОВЛЕНИЕ: все еще ничего не сделал с неблокирующим назначением, но я действительно изменил способ, которым я обновляю ПК, добавив next_PC в мой код. Проблема оператора I f исчезла. Тем не менее, проблема в том, что когда происходит скачок или переход, следующий ПК рассчитывается правильно, но инструкция в целевой ветви не будет получена в те же часы! пожалуйста, посмотрите на это фото
и предполагается, что это процессор MIPS с одним циклом, так что инструкция должна выбираться и выполняться за один цикл. поэтому задержка, вызванная неблокирующим присваиванием, нежелательна!
always @(*)
begin
IR <= Instruction;
end
always @(posedge clk)
begin
PC = next_PC;
OverFlow = 0;
end
// Decode + Operand Fetch //
always @ (IR)
begin
Op_code = IR[31:26];
if (Op_code == 6'd0) //R-type Instructions
begin
Func = IR[5:0];
if (Func == 6'b100000) //ADD
begin
ALU_op = 6'd1; //as numbered in LAB manual
read_addr1 = IR [25:21]; //Rs
read_addr2 = IR [20:16]; //Rt
end //end of ADD
if (Func == 6'b100001) //ADDU
begin
ALU_op = 6'd2; //as numbered in LAB manual
read_addr1 = IR [25:21]; //Rs
read_addr2 = IR [20:16]; //Rt
end //end of ADDU
/* some code here skipped to make the code shorter*/
if (Op_code == 6'b000100) //BEQ (Branch if Equal)
begin
ALU_op = 6'd30; //as numbered in LAB manual
read_addr1 = IR [25:21]; //Rs
read_addr2 = IR [20:16]; //Rt
Imm_32 = {{16{IR[15]}},IR [15:0]}; //Offset 2nd operand: imm-32 (Sign Extended Imm_16)
//BT = PC + Imm_32;
end
if (Op_code == 6'b000101) //BNE (Branch if NOT Equal)
begin
ALU_op = 6'd31; //as numbered in LAB manual
read_addr1 = IR [25:21]; //Rs
read_addr2 = IR [20:16]; //Rt
Imm_32 = {{16{IR[15]}},IR [15:0]}; //Offset 2nd operand: imm-32 (Sign Extended Imm_16)
end
if (Op_code == 6'b000001)
begin
if ( IR [20:16] == 5'b00001) //BGEZ (Branch on Greater than or Equal to Zero)
begin
ALU_op = 6'd33; //as numbered in LAB manual
read_addr1 = IR [25:21]; //Rs
Imm_32 = {{16{IR[15]}},IR [15:0]}; //Offset 2nd operand: imm-32 (Sign Extended Imm_16)
end
if (IR [20:16] == 5'b10000) //BLTZAL (Branch on less than Zero And Link)
begin
ALU_op = 6'd34; //as numbered in LAB manual
read_addr1 = IR [25:21]; //Rs
Imm_32 = {{16{IR[15]}},IR [15:0]}; //Offset 2nd operand: imm-32 (Sign Extended Imm_16)
end
end
if (Op_code == 6'b000010) //J (Jump)
begin
ALU_op = 6'd35; //as numbered in LAB manual
Imm_32 = {PC [31:26],IR [25:0]}; //Jump Address
end
if (Op_code == 6'b000011) //JAL (Jump And Link)
begin
ALU_op = 6'd36; //as numbered in LAB manual
Imm_32 = {PC [31:26],IR [25:0]}; //Jump Address
end
end // end of DECODE
// Execution & Write Back//
always @ (ALU_op, ALU_in1, ALU_in2)
begin
next_PC = PC+1;
wr_En = 0;
DM_wrEn_0 = 0;
DM_wrEn_1 = 0;
DM_wrEn_2 = 0;
DM_wrEn_3 = 0;
if (ALU_op == 6'd1) //ADD
begin
ALU_out = ALU_in1 + ALU_in2;
write_addr = IR [15:11]; //Rd
if( ( (ALU_in1[31]) && (ALU_in2[31]) && (!ALU_out[31]) )||( (!ALU_in1[31]) && (!ALU_in2[31]) && (ALU_out[31]) ) )
OverFlow = 1'b1;
wr_En = 1;
write_data = ALU_out;
end
if (ALU_op == 6'd2) //ADDU
begin
ALU_out = ALU_in1 + ALU_in2;
write_addr = IR [15:11]; //Rd
wr_En = 1;
write_data = ALU_out;
end
/* some code here skipped to make it shorter*/
if (ALU_op == 6'd30) //BEQ
begin
ALU_out = ((ALU_in1) == (ALU_in2));
if (ALU_out == 32'd1)
begin
//PC = PC + Imm_32;
next_PC = PC + Imm_32;
BT = 32'd56;
//PC = BT;
end
end
if (ALU_op == 6'd31) //BNE
begin
ALU_out = ((ALU_in1) == (ALU_in2));
if (ALU_out == 0)
begin
//PC = PC + Imm_32;
next_PC = PC + Imm_32;
BT = 32'd90;
end
end
if (ALU_op == 6'd33) //BGZE
begin
ALU_out = ((ALU_in1) >= 0);
if (ALU_out)
begin
//PC = PC + Imm_32;
next_PC = PC + Imm_32;
end
end
if (ALU_op == 6'd34) //BLTZAL
begin
ALU_out = ((ALU_in1) < 0);
if (ALU_out)
begin
write_addr = 5'd31; //$ra ($31)
write_data = PC;
wr_En = 1;
//PC = PC + Imm_32;
next_PC = PC + Imm_32;
end
end
if (ALU_op == 6'd32) //JR
begin
//PC = ALU_in1;
next_PC = ALU_in1;
end
if (ALU_op == 6'd35) //J
begin
//PC = Imm_32;
next_PC = Imm_32;
end
if (ALU_op == 6'd36) //JAL
begin
write_addr = 5'd31; //$ra
wr_En = 1;
write_data = PC;
//PC = Imm_32;
next_PC = Imm_32;
end
end //end of EXE and WB always block
1 ответ
Теперь, когда мы знаем, что вы хотите, чтобы это был один цикл, это многое проясняет.
Во-первых, вам нужно решить, где регистры - для связи с памятью, вы помещаете адрес для доступа к памяти инструкций в регистр или к полученным инструкциям, потому что прямо сейчас с вашим always @(*) IR <= Instruction; end
а также always @(posedge clk) begin PC = nextPC end
Вы делаете странную комбинацию обоих. Вам нужно сделать либо:
always @(posedge clk) begin
IR <= Instruction;
PC <= nextPC;
end
И адрес памяти инструкций с nextPC
или же:
always @(posedge clk) begin
PC <= nextPC;
end
assign IR = Instruction;
И адрес памяти инструкций с PC
, Обратите внимание, что комментарий Моргана о том, что чтение из памяти должно занимать цикл, в целом корректен, однако мы будем предполагать, что у вас есть какая-то комбинационная память, которая не требует фиксированного адреса и вывода / ввода. В реальных системах вы действительно должны быть готовы иметь регистры по адресу и шине ввода / вывода в память.
Несколько других стилистических заметок:
- Лучше не писать собственные списки чувствительности, используйте
always @(*)
неalways @(IR)
для комбинационной логики (используяalways @(posedge clk)
для последовательной логики. - Использовать блокирующее назначение (
=
) вalways @(*)
(комбинационные) блоки и НБА (<=
) вalways @(posedge clk)
(последовательные) блоки. - Используйте операторы, а не длинный список
if .. if .. if ..
для декодирования кодов операций. Его гораздо легче читать - Я предлагаю заменить коды операций параметрами или макросами, чтобы код читался легче (например, вместо
if (op == 6'b001001)
, ты получаешьparameter ALU_ADD = 6'b001001; ... if (op == ALU_ADD)
намного понятнее; еще лучше в случае запутыванияcase (op) ALU_ADD: ... ALU_SUB: ... ALU_OR: ... endcase
)