Compartir datos en dos bloques always @ (posedge ...) diferentes

0

Tengo las siguientes dos señales A y B

      __________                                           __________
_____|          |_________________________________________|          |_____
 __                   __    __    __    __    __    __                  __
|  |_________________|  |__|  |__|  |__|  |__|  |__|  |________________|

Tenga en cuenta que:

  • No hay un reloj común entre ellos disponible
  • La señal B siempre cambia cuando A es baja y A siempre cambia cuando B es baja

Me gustaría crear un contador de 6 bits que incremente su valor en cada flanco negativo de la señal A y luego emita su valor a nivel de bits en el medio, exactamente en los bordes positivos de la señal B.

El problema es que tengo que compartir un estado entre estos dos, por ejemplo:

wire bit_output;
reg [5:0] counter;
reg [5:0] current_value;
always @(posedge signalA) begin
  counter <= counter + 1;
  current_value <= counter;
end

always @(posedge signalB) begin
  current_value <= { current_value[4:0], 1'b0 };
end

assign bit_value = current_value[5];

Desafortunadamente, este no es un código de Verilog válido, por ejemplo, en Xilinx ISE obtengo:

Signal current_value[5] in unit test is connected to following multiple drivers:

Intenté colocarlos en una declaración always pero esto tampoco es posible:

Assignment under multiple single edges is not supported for synthesis

Si tuviera un reloj común, podría usar una máquina de estados convencional, pero en este caso solo tengo estas dos señales independientes.

No importa con qué solución surja, siempre necesito compartir datos de alguna manera.

No quiero ejecutar ambas rutas de forma independiente; por ejemplo, si tiene un contador para SignalB y si es cero, inicialice con datos de A porque la condición inicial sería indefinida. Me gustaría asegurarme de que los datos que se escupen en los bordes positivos de la señal B siempre se inicialicen correctamente en el borde descendente de la señal A incluso si , por ejemplo, solo aparecen 3 bordes de señal B positivos .

¿Cuál es la forma más fácil de implementar esto?

(esto es solo un ejemplo simplificado para mantener las cosas compactas; la tarea real es más complicada)

    
pregunta divB

2 respuestas

1

Para hacer esto en un FPGA, la forma habitual sería introducir un nuevo reloj, que funcione mucho más rápido que A o B. Registre su lógica desde este nuevo reloj y use A y B solo como señales de control, no como relojes.

El nuevo reloj debe ser lo suficientemente rápido para garantizar que haya al menos un borde para cada cambio de valor de las entradas A y B.

module weird_counter(A, B, CLK, Q);
input A, B;
input CLK;
output Q;

reg [2:0] Ad;  
reg [2:0] Bd; /* Delayed copies of A and B */
reg [5:0] ctr;
reg [5:0] obuf;

assign Q = obuf[5];

always @(posedge CLK) begin
    Ad <= {Ad[1:0], A};
    Bd <= {Bd[1:0], B};

always @(posedge CLK) begin
    if(Ad[1] & ~Ad[2]) begin /* We are seeing a rising edge on A */
         /* Do something with ctr and obuf*/
    end
    if(Bd[1] & ~Bd[2]) begin /*Rising edge on B */
         /* Do something else with ctr and obuf*/
    end
end

endmodule;

Editar : se agregaron algunas etapas a Ad y Bd a la sugerencia de Tom Carpenter para demostrar la sincronización de una señal a través de los límites del reloj. Esto reduce el riesgo de una falla lógica debido a una violación de los tiempos de configuración y retención.

    
respondido por el The Photon
0

Si utilizo esta especificación:
La señal B siempre cambia cuando A es baja y A siempre cambia cuando B es baja, me gustaría crear un contador de 6 bits que incremente su valor en cada borde negativo de señal A y luego emite su valor en modo bit a punto, exactamente en los bordes positivos de la señal B.

Obtengo este código:

always @(negedge A)
   if (B==0)
      counter <= counter + 1;

always @(posedge B)
   if (A==0)
     bit_out <= bit_out + 1;

assign serial_out = counter[bit_out];

Si puede garantizar que la transición no ocurra cuando las señales sean altas, puede omitir las dos secciones "if (A / B == 0)".
En cuanto a en el medio, exactamente en los bordes positivos de la señal . Debes controlar eso haciendo correctamente las señales A y B.

Agregaste:

si, por ejemplo, solo aparecen 3 bordes de señal B positivos. En este caso, el próximo ciclo aún debe tener un nuevo comienzo. bit_out se debe establecer en cero con la primera instrucción always @

Aún puedes hacer eso usando A como un reinicio sincrónico:

always @(posedge B or posedge A)
   if (A)
      bit_out <= 0; // reset counter when A is high 
   else
      bit_out <= bit_out + 1;

El flanco ascendente de B no debe coincidir con el flanco descendente de A.

Pero llega un momento en que no puedes hacer estas cosas con lógica puramente sincrónica. Por ejemplo, I2C es muy, muy difícil de hacer en lógica Verilog pura y la mayoría de las veces la interfaz se ejecuta utilizando un reloj a una frecuencia mucho mayor.

    
respondido por el Oldfart

Lea otras preguntas en las etiquetas