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)
Другие вопросы по тегам