Soy bastante nuevo en Verilog, diseño de hardware y arquitectura de computadoras. Sin embargo, he intentado diseñar un procesador MIPS simplificado. Parece funcionar bien en su mayoría, pero cada vez que lo simulo, se cuelga de una instrucción BEQ.
Estoy tratando de hacer que ejecute el siguiente programa (R1 está precargado con 32'd10 y R4 está precargado con 32'd1):
0 ADD R0 R1 R5; // R1 = 10
4 ADD R0 R1 R5; // R5 = 10 (redundant)
8 ADD R5 R6 R6; // R6 = R5 + R6
C SUB R5 R4 R5; // R5 = R5 - 1
10 SLT R4 R5 R7; // R4 < R5
14 BEQ R7 R4 -3; // Loop back to 8 if R5 > 1
Aquí está la salida de GTKWave:
Según tengo entendido, los 16 bits inferiores de la instrucción deben ir a sign_extend, donde se concatena con 16 copias del bit de signo. Esto luego va a shift_left2, donde la dirección se multiplica por 4. Esto se suma a la dirección anterior + 4.
Los dos operandos enviados a la ALU son iguales, por lo que se afirma la señal z0. La señal de bifurcación es afirmada por el módulo de control, en base a los bits [31:26] de la instrucción. La rama y el cero tienen AND en branch_and, lo que hace que branchnd se afirme. Esto se alimenta a un mux para seleccionar la dirección de rama en lugar de la dirección anterior incrementada y se retroalimenta al contador del programa, se asigna a next_address en el siguiente ciclo de reloj y se alimenta a la memoria de instrucciones para obtener la siguiente dirección.
Parece que todo esto sucede en mi simulación. La dirección bifurcada se puede ver en el complemento a los 12 segundos en la imagen de arriba, por lo que parece funcionar correctamente hasta ese momento. Sin embargo, no puedo entender por qué se cuelga. Seguramente esto debería introducirse en el contador del programa, asignarse a next_address en el siguiente posedge del reloj y utilizarse para obtener la instrucción. Si cambio el programa para que no se tome la rama, continúa ejecutándose hasta que se queda sin instrucciones, por lo que parece estar relacionado con tomar la rama.
Intenté cambiar las listas de sensibilidad, agregar/eliminar el reloj de los módulos y casi cualquier otra cosa que se me ocurra. Tal vez me estoy perdiendo algo obvio. Agradecería mucho si alguien pudiera ayudar. Soy nuevo aquí, así que si esta no es la forma correcta de hacer una pregunta o si hay algo que pueda hacer para que esta pregunta sea más fácil de responder, házmelo saber.
Gracias
module MIPS_tb();
wire [4:0] rs1, rt1, rd1, writereg1;
wire [31:0] ins, register0, register1, register4, register5, register6, register7, addin, aluout, nextadd,
op1, op2, rfoutput2, dmreaddata, se_address, wdata, braddr, shaddr, incPCaddr;
wire reg_write, branch, branchnd, z0, regds, memtoreg, memwrite, alusrc;
reg clock;
reg reset;
MIPS mips1(clock, reset, rs1, rt1, rd1, writereg1, reg_write, ins,
register1, register4, register5, register6, register7, branch, branchnd, z0, addin, aluout, nextadd,
op1, op2, rfoutput2, dmreaddata, se_address, wdata, braddr, shaddr, incPCaddr, regds, memtoreg, memwrite, alusrc);
initial
begin
$dumpfile("MIPS_MIPS_tb.vcd");
$dumpvars(0, clock, reset, rs1, rt1, rd1, writereg1, reg_write, ins,
register1, register4, register5, register6, register7, branch, branchnd, z0, addin, aluout, nextadd,
op1, op2, rfoutput2, dmreaddata, se_address, wdata, braddr, shaddr, incPCaddr, regds, memtoreg, memwrite, alusrc);
reset = 1'b1;
clock = 1'b0;
#1
reset = 1'b0;
repeat(250)
#1
clock = !clock;
#1
$finish;
end
endmodule
MIPS
module MIPS(clock, reset, rs1, rt1, rd1, writereg1, reg_write, ins,
register1, register4, register5, register6, register7, branch, branchnd, z0, addin, aluout, nextadd,
op1, op2, rfoutput2, dmreaddata, se_address, wdata, braddr, shaddr, incPCaddr, regds, memtoreg, memwrite, alusrc);
input clock, reset;
output [4:0] rs1, rt1, rd1, writereg1;
output [31:0] ins, addin, aluout;
output reg_write;
output [31:0] register1, register4, register5, register6, register7, nextadd, op1, op2,
rfoutput2, dmreaddata, se_address, wdata, braddr, shaddr, incPCaddr;
output branch, branchnd, z0, regds, memtoreg, memwrite, alusrc;
wire [31:0] instruction, next_address, address_in, operand1, operand2, reg_file_output2,
ALU_result, data_mem_read_data, sign_extended_address, write_data,
branch_address, shifted_address, incremented_PC_address;
wire RegDst, Branch, MemtoReg, MemWrite, ALUSrc, RegWrite, zero, BEQ;
wire [1:0] ALUOp;
wire [4:0] rs, rt, rd, write_register;
wire [5:0] funct;
wire [15:0] addressop;
wire [3:0] ALU_control_vector;
assign rs1 = rs;
assign rt1 = rt;
assign rd1 = rd;
assign writereg1 = write_register;
assign reg_write = RegWrite;
assign ins = instruction;
assign z0 = zero;
assign branch = Branch;
assign branchnd = BEQ;
assign addin = address_in;
assign aluout = ALU_result;
assign nextadd = next_address;
assign op1 = operand1;
assign op2 = operand2;
assign rfoutput2 = reg_file_output2;
assign dmreaddata = data_mem_read_data;
assign se_address = sign_extended_address;
assign wdata = write_data;
assign braddr = branch_address;
assign shaddr = shifted_address;
assign incPCaddr = incremented_PC_address;
assign regds = RegDst;
assign memtoreg = MemtoReg;
assign memwrite = MemWrite;
assign alusrc = ALUSrc;
program_counter pc1(next_address, address_in, clock, reset);
register_file rf1(operand1, reg_file_output2, rs, rt, write_register, write_data, RegWrite,
register1, register4, register5, register6, register7);
ALU alu1(ALU_result, zero, operand1, operand2, ALU_control_vector);
ALU_control aluctrl1(ALU_control_vector, ALUOp, funct);
data_memory dm1(data_mem_read_data, reg_file_output2, ALU_result, MemWrite);
sign_extend se1(sign_extended_address, addressop);
instruction_memory im1(instruction, next_address);
control ctrl1(RegDst, Branch, MemtoReg, ALUOp, MemWrite, ALUSrc, RegWrite, instruction,
rs, rt, rd, funct, addressop);
branch_adder ba1(branch_address, shifted_address, incremented_PC_address);
PC_increment pci(incremented_PC_address, next_address);
branch_and brand(BEQ, Branch, zero);
shift_left2 shl2(shifted_address, sign_extended_address);
mux_2to1 branch_mux(address_in, BEQ, incremented_PC_address, branch_address);
mux_2to1_5bit write_reg_mux(write_register, RegDst, rt, rd);
mux_2to1 write_data_mux(write_data, MemtoReg, ALU_result, data_mem_read_data);
mux_2to1 ALU_source_mux(operand2, ALUSrc, reg_file_output2, sign_extended_address);
endmodule
Unidad de control
module control(RegDst, Branch, MemtoReg, ALUOp, MemWrite, ALUSrc, RegWrite, instruction,
rs, rt, rd, funct, addressop);
output RegDst, Branch, MemtoReg, MemWrite, ALUSrc, RegWrite;
output [4:0] rs, rt, rd;
output [5:0] funct;
output [15:0] addressop;
output [1:0] ALUOp;
input [31:0] instruction;
wire [5:0] Op = instruction[31:26];
assign rs = instruction[25:21];
assign rt = instruction[20:16];
assign rd = instruction[15:11];
assign funct = instruction[5:0];
assign addressop = instruction[15:0];
reg [7:0] Control;
assign RegDst = Control[7];
assign RegWrite = Control[6];
assign ALUSrc = Control[5];
assign MemWrite = Control[4];
assign MemtoReg = Control[3];
assign Branch = Control[2];
assign ALUOp = Control[1:0];
initial
Control = 7'd0;
always @(*)
casex(Op)
32'd0 : Control = 8'b11000010; // R-TYPE
32'd35 : Control = 8'b01101000; // LW
32'd43 : Control = 8'bx011x000; // SW
32'd4 : Control = 8'bx000x101; // BEQ
default : Control = 8'b00000000; // NOP
endcase
endmodule
Memoria de instrucciones
module instruction_memory(instruction, address);
output reg [31:0] instruction;
input [31:0] address;
reg [31:0] prog [40:0];
initial
begin
prog[0] <= 32'b000000_00000_00001_00101_00000000000;
prog[4] <= 32'b000000_00000_00001_00101_00000000000;
prog[8] <= 32'b000000_00000_00000_00110_00000000000;
prog[12] <= 32'b000000_00101_00110_00110_00000000000;
prog[16] <= 32'b000000_00101_00100_00101_00000000010;
prog[20] <= 32'b000000_00100_00101_00111_00000001010;
prog[24] <= 32'b000100_00111_00100_11111_11111111100;
prog[28] <= 32'b000000_00101_00100_00101_00000000010;
prog[32] <= 32'b000000_00101_00100_00111_00000001010;
prog[36] <= 32'b000000_00100_00110_00110_00000000000;
prog[40] <= 32'b000000_00100_00110_00110_00000000000;
end
always @(address)
instruction = prog[address];
endmodule
firmar extender
module sign_extend(sign_extended_address, instruction_addr);
output reg [31:0] sign_extended_address;
input [15:0] instruction_addr;
always @(*)
begin
sign_extended_address[15:0] = instruction_addr;
if(instruction_addr[15]==1'b1)
sign_extended_address[31:16] = 16'b1111_1111_1111_1111;
else
sign_extended_address[31:16] = 16'b0000_0000_0000_0000;
end
endmodule
Unidad de control ALU
module ALU_control(ALU_control_vector, ALUOp, funct);
output reg [3:0] ALU_control_vector;
input [1:0] ALUOp;
input [5:0] funct;
initial
ALU_control_vector = 4'b0000;
always @(*)
begin
case(ALUOp[1])
1'b0 : case(ALUOp[0])
1'b0 : ALU_control_vector = 4'b0010; // ADD for LW, SW
1'b1 : ALU_control_vector = 4'b0110; // SUB for BEQ
endcase
1'b1 : case(funct[3:0])
4'b0000 : ALU_control_vector = 4'b0010; // ADD
4'b0100 : ALU_control_vector = 4'b0000; // AND
4'b0101 : ALU_control_vector = 4'b0001; // OR
4'b0010 : ALU_control_vector = 4'b0110; // SUB
4'b1010 : ALU_control_vector = 4'b0111; // SLT
default : ALU_control_vector = 4'b0010; // ADD (doesn't matter)
endcase
endcase
end
endmodule
ALU
module ALU(ALU_result, zero, operand1, operand2, ALU_control_vector);
output reg [31:0] ALU_result;
output reg zero;
input [31:0] operand1, operand2;
input [3:0] ALU_control_vector;
initial
begin
ALU_result = 32'd0;
zero = 0;
end
always @(*)
begin
case(ALU_control_vector)
4'b0000 : ALU_result = operand1 & operand2;
4'b0001 : ALU_result = operand1 | operand2;
4'b0010 : ALU_result = operand1 + operand2;
4'b0110 : ALU_result = operand1 - operand2;
4'b0111 : ALU_result = (operand1 < operand2) ? 32'd1 : 32'd0;
endcase
if (ALU_result == 0)
zero = 1'b1;
else
zero = 1'b0;
end
endmodule
Registrar archivo
module register_file(operand1, reg_file_output2, rs, rt, write_register, write_data, RegWrite,
register1, register4, register5, register6, register7);
output reg [31:0] operand1, reg_file_output2;
output [31:0] register0, register1, register4, register5, register6, register7;
input [31:0] write_data;
input [4:0] rs, rt, write_register;
input RegWrite;
reg [31:0] registers [31:1]; // Register 0 reserved for "0"
assign register1 = registers[1];
assign register4 = registers[4];
assign register5 = registers[5];
assign register6 = registers[6];
assign register7 = registers[7];
initial
begin
registers[1] = 10;
registers[4] = 1;
registers[5] = 0;
registers[6] = 0;
registers[7] = 0;
operand1 = 0;
reg_file_output2 = 0;
end
always @(*)
begin
operand1 = (rs == 0) ? 32'd0 : registers[rs];
reg_file_output2 = (rt == 0) ? 32'd0 : registers[rt];
if(RegWrite)
registers[write_register] = write_data;
end
endmodule
Contador de programa
module program_counter(next_address, address, clock, reset);
output [31:0] next_address;
input [31:0] address;
input clock, reset;
reg [31:0] pc_next;
assign next_address = pc_next;
always @(posedge clock, posedge reset)
begin
if(reset)
pc_next = 32'd0;
else
pc_next = address;
end
endmodule
Sumador de ramas
module branch_adder(branch_address, shifted_addr_instruction, incremented_PC_addr);
output [31:0] branch_address;
input [31:0] shifted_addr_instruction, incremented_PC_addr;
assign branch_address = shifted_addr_instruction + incremented_PC_addr;
endmodule
Mux 1
module mux_2to1(out, select, a, b);
output reg [31:0] out;
input select;
input [31:0] a, b;
initial
out = 0;
always @(*)
begin
if(select)
out = b;
else
out = a;
end
endmodule
Mux 2
module mux_2to1_5bit(out, select, a, b);
output reg [4:0] out;
input select;
input [4:0] a, b;
initial
out = 0;
always @(*)
begin
if(select)
out = b;
else
out = a;
end
endmodule
Incrementador de PC
module PC_increment(incremented_PC_address, current_address);
output [31:0] incremented_PC_address;
input [31:0] current_address;
assign incremented_PC_address = current_address + 4;
endmodule
Rama/Cero Y
module branch_and(BEQ, branch, zero);
output BEQ;
input branch, zero;
and(BEQ, branch, zero);
endmodule
Dirección de desplazamiento a la izquierda
module shift_left2(shifted_address, sign_extended_address);
output [31:0] shifted_address;
input [31:0] sign_extended_address;
assign shifted_address = sign_extended_address << 2;
endmodule
Memoria de datos
module data_memory(data_mem_read_data, data_mem_write_data, ALU_result, MemWrite);
output reg [31:0] data_mem_read_data;
input [31:0] ALU_result, data_mem_write_data;
input MemWrite;
wire MemRead = ~MemWrite;
reg [31:0] data_registers [255:0];
initial
data_mem_read_data = 0;
always @(ALU_result, data_mem_write_data)
begin
if(MemWrite)
data_registers[ALU_result] = data_mem_write_data;
if(MemRead)
data_mem_read_data = data_registers[ALU_result];
end
endmodule
En cualquier bloque secuencial siempre, debe usar una asignación sin bloqueo (el signo menor que/igual, <=). Si no es la causa de este problema, seguramente causará una capa.
La regla es simple: si un proceso escribe en una variable sincronizada con un evento y otro proceso lee la misma variable sincronizada con el mismo evento, debe escribir usando un NBA asegurándose de que el proceso de lectura use el valor anterior de la variable. Si, en cambio, usa una asignación de bloqueo en esta situación, existe una condición de carrera entre obtener el valor nuevo o antiguo de la variable porque el orden de ejecución entre los procesos de lectura y escritura es indeterminado.
Por "evento" me refiero a un borde de un reloj o habilitar. Y por "igual" me refiero a la expresión idéntica o combinada de la misma señal.
chris stratton
Ctuohey
Mate
Mate
chris stratton
Ctuohey