¿Por qué el diseño de mi ALU demora el envío de los resultados de dos ciclos de reloj desde la entrada de datos válidos?

2

Hola EE StackExchange!

Hace varios meses que intento diseñar una CPU simple de 8 bits. Sin embargo, estoy experimentando un problema: la ALU genera el resultado de la operación dos ciclos de reloj después de que se ha presentado con datos válidos. ¿Por qué sucede esto y, lo que es más importante, hay alguna manera de rectificar este comportamiento? La arquitectura FPGA contra la que estoy simulando es Spartan 3, y la ALU está escrita en verilog.

El código siguiente:

module alu  (input [7:0] a,b,
             input [3:0] opcode,
             input clk, reset,
             output reg [7:0] y,
             output reg cout);
        /* Decode the instruction */
        always @(posedge clk)
        if(reset == 1)
        begin
                y <= 8'b0;
                cout <= 0;
        end
        else if(reset == 0)
        begin
                case (opcode)
                        4'h00 /* OR */:   y <= a | b;
                        4'h01 /* AND */:   y <= a & b;
                        4'h02 /* NOTA */:   y <= ~a;
                        4'h03 /* XOR */:   y <= a ^ b;
                        4'h04 /* ADD */:   y <= a - b;
                        4'h05 /* SUB */:   y <= a - b;
                endcase
        end
endmodule //alu
    
pregunta infiniteNOP

2 respuestas

4

Si desea obtener resultados inmediatos de su ALU, entonces no utilice un proceso cronometrado:

module alu (
  input [7:0] a,
  input [7:0] b,
  input [3:0] opcode,
  output reg [7:0] y
);

  /* Decode the instruction */
  always @* begin
    case (opcode)
      4'h00 /* OR */:   y <= a | b;
      4'h01 /* AND */:   y <= a & b;
      4'h02 /* NOTA */:   y <= ~a;
      4'h03 /* XOR */:   y <= a ^ b;
      4'h04 /* ADD */:   y <= a + b;
      4'h05 /* SUB */:   y <= a - b;
    endcase
  end

endmodule
    
respondido por el Dave Tweed
2

Aquí hay un pequeño banco de pruebas que preparé para su diseño (también soy un principiante):

'timescale 1 ns / 1 ns

module multiplierTest ( );

reg [7 : 0] a, b ;
reg clk , reset ;
reg [3:0] opcode  ;
wire [7 : 0] y ; 
wire [7 : 0] cout ; 


alu  multiplier_uut         (   .a(a),
                                .b(b),
                            .clk(clk),
                            .opcode(opcode), 
                            .reset(reset),
                            .y(y),
                            .cout(cout)
                        ) ;

//Initialise registers 
initial 
begin
clk = 1'b0 ;

a = 'h00 ;
b = 'h00 ;
opcode = 'h0 ;
reset = 1'b1 ;
end


initial
begin
repeat (4) @ (negedge clk) ;
reset = 0 ;
@ (negedge clk) ;
a = $random ;
b = $random ;
opcode = 'h3 ;
repeat (4) @ (negedge clk) ;
$stop ;
end

//clock
always #25 clk = ~clk ;

endmodule                                   

Si solo copia y pega esto y ejecuta, verá que su salida es la esperada. Me gustaría señalar algunas cosas (que he aprendido de mi corta experiencia de verilog):

  • Si desea que su salida aparezca en el borde del reloj, haga que la entrada esté disponible antes de eso. De lo contrario, no se cumplirán las restricciones de tiempo (configuración y tiempos de espera). Personalmente, me gusta que las entradas estén disponibles con 1 borde de reloj por adelantado. Así que aquí en su alu, la salida debe ser afirmada en posedge; Así que he puesto a disposición las entradas de antemano en negedge. Ahora no sé si esto es bueno o malo, pero parece que siempre funciona conmigo.

  • Siempre debe dar una declaración predeterminada en su caso e incluir todos los valores predeterminados de las salidas. De lo contrario, durante la síntesis, se deducirán los latches.

  • Tampoco ha hecho uso de la variable cout (como lo señaló Tom Carpenter) y sus tamaños de variables no coinciden. Deben ser 4'h1, no 4'h01, ya que 1 hex es de 4 bits.

  • Otra cosa con la que siempre me encuentro al sintetizar mi diseño es que no se permiten conexiones reg en entradas y salidas. Xilinx siempre se queja y quiere que sean cables. Así que puedes hacer que se transfieran, declarar un registro para tu bloqueo siempre y hacer

    assign output = output_reg

(Por favor, avíseme si parece contradecir algo)

    
respondido por el Plutonium smuggler

Lea otras preguntas en las etiquetas