eliminando inout de las matrices de puertos

1

Esta pregunta está en el contexto del uso de verilog / systemverilog para RTL sintetizable.

Tengo algunas señales vectoriales que van a través de los límites de los módulos que actualmente están definidos como puertos de entrada. La razón por la que están en los puertos de salida es que algunos de los índices vectoriales se asignan en algunos lugares dentro de la jerarquía, mientras que otros se asignan en otros lugares. Sin embargo, es enormemente beneficioso poder considerarlos como un gran autobús compartido que puede dirigir a muchos lugares.

El problema es que estoy usando estos puertos de entrada en RTL sintetizado y algunas de las herramientas de back-end tienen problemas con estas entradas cuando se trata de síntesis de baja potencia y comprobación de equivalencia lógica.

En realidad, solo hay un controlador para estas señales, por lo que nunca es necesario que entren. El problema es que algunos índices se marcarían idealmente como entrada, mientras que otros se marcarán como salida.

En el pasado, una solución ha sido utilizar vectores dispersos, uno para cada ubicación jerárquica que podría estar asignando uno de estos índices. Luego, todos se combinan, utilizando bloques de generación y máscaras de ubicación para determinar cómo asignar cada uno de los posibles controladores a la señal final.

Este enfoque termina siendo una gran sobrecarga y requiere hacer algún nivel de enmascaramiento / mapeo en cada ubicación jerárquica, y todo esto es solo para evitar tener puertos controlados individualmente como direcciones internas.

Dado que el flujo de mi herramienta de back-end no requiere el uso de puertos de entrada en este escenario, ¿alguien tiene otro enfoque que pueda ser más eficiente en la codificación?

Aquí hay un código de ejemplo para ilustrar (no probado):

Mi código ideal es el siguiente:

module driver0(inout [1:0] sig);
    assign sig[0] = 'b1;  // drive with some real value
endmodule

module driver1(inout [1:0] sig);
    assign sig[1] = 'b1;  // drive with some real value
endmodule

module top();
    wire sig[1:0];
    driver0 u0(.sig(sig));
    driver1 u1(.sig(sig));
endmodule

Ningún bit del sig vector es multi-accionado, solo son manejados en diferentes módulos.

Mi solución no inout es la siguiente:

module driver0(output [1:0] sig);
    assign sig[0] = 'b1;  // drive with some real value
    assign sig[1] = 'b0;  // clear unused indices
endmodule

module driver1(output [1:0] sig);
    assign sig[1] = 'b1;  // drive with some real value
    assign sig[0] = 'b0;  // clear unused indices
endmodule

module top();
    wire [1:0] sig, sig0, sig1;
    driver0 u0(.sig(sig0));
    driver1 u1(.sig(sig1));

    assign sig = sig0 | sig1;
endmodule

Requiere tener un vector disperso temporal para cada controlador posible y luego combinarlos. Esto fue solo un ejemplo con un vector de 2 bits y dos controladores. Puede imaginar que requiere bastante más esfuerzo y desperdicio de código cuando el vector es grande, ya que requiere un bucle sobre cada índice y luego tener algún mecanismo para determinar qué bits se activan en cada nivel jerárquico.

    
pregunta mattgately

1 respuesta

1

La solución más sencilla es la salida dividida y la entrada para los módulos de controladores.

module driver0 #(paramerter N=8)(input [N-1:0] sig_in, output sig_out);
    assign sig_out = 'b1;  // drive with some real value
endmodule

module driver1 #(paramerter N=8)(input [N-1:0] sig_in, output sig_out);
    assign sig_out = 'b1;  // drive with some real value
endmodule

Puede permitir que la salida también sea una entrada del mismo módulo en top

module top();
    paramerter N=8;
    wire [N-1:0] sig;
    driver0 #(N) u0(.sig_in(sig), .sig_out(sig[0]));
    driver1 #(N) u1(.sig_in(sig), .sig_out(sig[1]));
endmodule

O puede evitar que la salida del controlador se retroalimente deslizando la matriz para la entrada. Esto puede tomar un poco más de precaución. Podrías dividir las señales de entrada como dos o más también. Cuanto más intuitivo sea, más fácil será la gestión del proyecto.

module top();
    paramerter N=8;
    wire [N-1:0] sig;
    driver0 #(N-1) u0(.sig_in(sig[N-1:1]), .sig_out(sig[0]));
    driver1 #(N-1) u1(.sig_in({sig[N-1:2],sig[0]}), .sig_out(sig[1]));
endmodule

Desde IEEE Std 1364-2001, Verilog permite algunas definiciones de puertos de lujo. Ver IEEE Std 1364-2001 § 12.3.3 Declaraciones de puertos o IEEE Std 1800 de SystemVerilog -2012 § 23.2.2 Declaraciones de puertos
El soporte para esto puede muy a través de herramientas ya que esta práctica no es común. Si su sintetizador tiene problemas con interface , también puede tener problemas con el alias de puertos.

module driver0 ( .sig({sig_in,sig_out}) );
  input sig_in;
  output sig_out;
  assign sig_out = 'b1;  // drive with some real value
endmodule

module driver1 ( .sig({sig_in,sig_out}) );
  input sig_in;
  output sig_out;
  assign sig_out = 'b1;  // drive with some real value
endmodule

module top();
    wire [1:0] sig;
    driver0 u0(.sig(sig));
    driver1 u1(.sig(sig));
endmodule

Si el bus atraviesa varios niveles de jerarquía, la solución anterior probablemente será tediosa de administrar. Se debe utilizar un interface . Algunas pautas para reducir las limitaciones de síntesis:

  • Defina todas las señales como un tipo logic . La única excepción son los tres estados donde se espera que los bits tengan dos o más controladores.
  • Defina todos los estados tri como wire o tri
  • Asigne todos los tipos logic dentro de un bloque always_ff , always_comb , always_latch (si el bloqueo es necesario. los tipos logic se pueden asignar con assign enunciados, pero no todas las herramientas validan los conflictos de conducción con %código%
  • Mantenga las tareas de los tres estados lo más simple posible.
    • Ej: assign
  • No utilice assign myif.io = drive_enable ? io_out : 'z; como puerto en el módulo más superior que se sintetizará. Muchos sintetizadores actuales aplanarán y localizarán interfaces con nombres escapados. Si es necesario, cree un módulo envoltorio como una capa de traducción entre la interfaz y la mayoría de las señales de puerto. Ej:

    interface my_interface(input clk, 
        /*May need to declare top port level tri-stats as an interface port*/
        inout [7:0] io, ...
        /*Optional output logic ... , input ... */ );
      logic in_a, in_b, out;
      //wire [7:0] io; // internal tri-state
      ...
    endinterface : my_interface
    module same_port #(parameter N=1) (a,a); // Synthesizer might support this, see manual
      inout [N-1:0] a;
    endmodule : same_port
    ...
    // RTL top with interface as port
    module my_top(my_interface myif);
      ...
      sub sub0 ( .myif(myif), .*);
      ...
    endmodule : my_top
    // wrapper to use for synthesis
    module synth_top( output logic out, input in_a, in_b, clk, inout [7:0] io, ...);
      my_interface myif( .* );
      my_top mytop( .myif(myif) );
    
      // connect myif sigs with port sigs thate are not already connected with .*
      always_comb begin : conn_in
        myif.in_a = in_a;
        myif.in_b = in_b;
        ...
      end : conn_in
      always_comb begin : conn_out
        out = myif.out;
        ...
      end : conn_out
      /* connect tri-state this way may not be supported by all synthesizers,
       * refer to manual */
      //same_port #(8) conn_inout_io(io, myif.io); 
      ...
    endmodule : synth_top
    
respondido por el Greg

Lea otras preguntas en las etiquetas