Задержка перехода на 1 команду в реализации Verilog и проблема синхронизации
У меня есть 16-битная реализация MIPS с одним циклом, очень редкая, над которой я работал в Verilog. Все работает, за исключением того факта, что ветвление задерживается на один полный тактовый цикл.
always @(posedge clock) begin
// Necessary to add this in order to ensure PC => PC_next
iaddr <= pc_next
end
Приведенный выше код используется для обновления счетчика программы / адреса команд, которые поступают из модуля PCLogic:
module PCLogic(
pc_next, // next value of the pc
pc, // current pc value
signext, // from sign extend circuit
branch, // beq instruction
alu_zero, // zero from ALU, used in cond branch
reset // reset input
);
output [15:0] pc_next;
input [15:0] pc;
input [15:0] signext; // From sign extend circuit
input branch;
input alu_zero;
input reset;
reg [15:0] pc_next;
always @(pc or reset) begin
if (reset == 1)
pc_next = 0;
else if (branch == 1 && alu_zero == 1)
pc_next = pc+2+(signext << 1);
else
pc_next = pc+2;
end
endmodule
iaddr
простой 16-битный регистр, в котором хранится счетчик программ
Я не понимаю, почему может быть проблема с этой схемой, но по какой-то причине вся схема задерживается на один такт, пока он не разветвляется (например, если у меня есть инструкция BEQ в 0x16, которая всегда переходит, она выполнится следующая инструкция в 0x18 и затем перейти к относительному смещению, но от 0x20).
Я почти чувствую, что решение прямо передо мной, но я не знаю, чего мне не хватает в семантике. Проблема смещения решается, если я удаляю +2
это всегда неявно, если только нет настоящего "пузыря" или аппаратного отказа, но задержка все еще присутствует.
Может кто-нибудь объяснить мне, что вызывает задержку и почему это происходит?
2 ответа
Ответ заключается в том, что использование состояния в модуле PCLogic вызовет дополнительную задержку распространения. Удаляя регистр в PCLogic, мы удаляем неявный шаг состояния в самом модуле, сокращая его распространение до пренебрежимо малой величины 0.
Таким образом, ответ должен измениться pc_next
рассчитывается always @(pc)
блок к одному на основе декларативных выражений:
wire [15:0] pc_next = (reset == 1)? 0 : (branch == 1 && alu_zero == 1)? pc+2+(signext << 1) : pc+2;
Изменяя нашу схему в комбинаторную схему, нам больше не нужно сохранять состояние и, таким образом, уменьшать "буфер" в нашем процессе. ПК теперь может обновляться в течение (T) времени вместо (2T).
Другой способ кодирования комбинационной схемы:
reg [15:0] pc_next;
always @* begin
if (reset == 1)
pc_next = 0;
else if (branch == 1 && alu_zero == 1)
pc_next = pc+2+(signext << 1);
else
pc_next = pc+2; // latch will be inferred without this
end
Это понадобится вам, когда ваша комбинационная схема станет более сложной, поскольку операторы присваивания трудно читать, когда имеется много вложенных if-else.
Примечание об этом
pc_next = pc+2; // latch will be inferred without this
Комбинационный блок должен иметь значение по умолчанию. Если в условном выражении не определено значение по умолчанию, оно сохранит свое значение и приведет к некорректному поведению. Комбинационный блок не должен содержать значение.
Для получения дополнительной информации о неожиданной защелке см. Это.