Bucle de retroalimentación en Verilog

Tengo un problema al escribir el código Verilog HDL. Quiero diseñar un controlador PID simple en FPGA Estoy usando la familia Cyclone II. Quiero retroalimentar mi valor de salida como entrada en una etapa previa de cálculos. La ecuación se ve así u(n) = u(n-1) + x (x se calcula correctamente), así que creé un registro para guardar el valor de salida y lo conecté a una etapa anterior en mi diseño.

Desafortunadamente, esta solución no funciona cuando simulo esto, obtengo un valor 'desconocido' en el registro en el que estoy guardando el valor de salida y el valor de salida en sí también es 'desconocido' (por desconocido me refiero a 'x').

¿Es posible crear un circuito de retroalimentación entre ciertos componentes en el diseño de HDL? ¿Cómo puede hacerse esto? ¿Debo tratar de resolver este problema haciendo los cálculos paso a paso en un FSM?

EDITAR: Este es mi código según lo solicitado

Módulo PID

module pid(clk,rst_n,Kp_in, Kd_in, Ki_in,temp_data_in,setpoint,pwm_out);


parameter N = 8;
parameter M = 16;

input   clk;
input   rst_n;
input   [N-1:0]temp_data_in;
input   [N-1:0]setpoint;    
input [N-1:0]Kp_in;
input [N-1:0]Kd_in;
input [N-1:0]Ki_in;

output [N-1:0]pwm_out;

wire    clk;
wire    rst_n;

reg [N-1:0]temp_data_reg;
reg [N-1:0]setpoint_reg;
reg [N-1:0]pwm_out_reg;

localparam [1:0] idle = 2'b00, load = 2'b01, run= 2'b10;
localparam k = 2;

reg [1:0] state_reg, state_next;

always@(posedge clk, negedge rst_n) begin
    if(!rst_n) begin
        state_reg <= idle;
    end else
        state_reg <= state_next;
    end

reg sum_out_old_reg_en;

reg     [N-1:0]K0;
reg [N-1:0]K1;
reg [N-1:0]K2;

wire [N-1:0] ex0;
wire [N-1:0] ex1;
wire [N-1:0] ex2;

wire [2*N-1:0] out0;
wire [2*N-1:0] out1;
wire [2*N-1:0] out2;

wire [2*N-1:0] sum1;
wire [2*N-1:0] sum2;
wire [2*N-1:0] sum_out_old;
wire [2*N-1:0] sum_out;

register e0(clk,rst_n,(temp_data_in-setpoint),ex0);
register e1(clk,rst_n,ex0,ex1);
register e2(clk,rst_n,ex1,ex2);
mult u_mult1(ex0,K0,out0);
mult u_mult2(ex1,K1,out1);
mult u_mult3(ex2,K2,out2);
adder u_adder1(out0,out1,sum1);
adder u_adder2(out2,sum_out_old,sum2);
adder u_adder3(sum1,sum2,sum_out);  

register16b u_old(clk,rst_n,sum_out,sum_out_old);

always@(posedge clk) begin
    state_next = state_reg; 
    case(state_reg)
        idle: begin
                    state_next = load;
                end
        load: begin
                    K0 = Kp_in + Kd_in + Ki_in;
                    K1 = -Kp_in + (-2)*Kd_in;
                    K2 = Kd_in;         
                    state_next = run;
                end
        run: begin

                state_next = run;
              end
    endcase
end

endmodule
¿Puedes compartir tu código? ¿Especificó un valor inicial para el registro de espera o utilizó una señal de reinicio para ponerlo en un estado conocido al comienzo de su simulación?
Sí, el valor de salida de los registros es 0 cuando el reinicio es bajo.
¿Y los conjuntos de su banco de pruebas de simulación se restablecen lo suficiente como para que todos los registros entren en un estado conocido, pero aún ve reaparecer los valores 'X' una vez que los suelta reset? Eso parece que hiciste todo bien --- si puedes, comparte tu código y podemos buscar errores tipográficos u otros tipos de problemas.
¿Está absolutamente seguro de que obtuvo el orden de sus entradas y salidas para los bloques instanciados, verdad? Sin conexiones explícitas (como reg reg1 (.CLK(c), .RST(r), .I(x), .O(y))), no hay forma de que pueda estar seguro de esta parte de su código.

Respuestas (2)

No veo ningún código que restablezca K0, K1y K2, hasta que se use el estado de carga.

Esto significa que en el arranque, estos registros tienen Xvalor.

Por lo tanto, las salidas de u_mult1, u_mult2y u_mult3tienen Xvalor.

Por lo tanto, la salida de u_adder1, u_adder2y u_adder3obtiene Xvalor.

Por lo tanto, en el siguiente borde del reloj, sum_out_oldobtiene Xvalor. Probablemente al mismo tiempo, las variables K se cargan con sus valores desde las entradas del módulo. Pero es muy tarde. Dado que sum_outse calcula a partir de sum_out_old, se convierte en X, y sum_out_oldqueda atrapada en el Xestado.

La manera fácil de resolver esto es tener K0, K1y K2restablecer a 0 cuando rst_nse afirma (bajo), pero debe considerar si esto es correcto para su situación.

Para la simulación, debe restablecer sus valores a un estado conocido al principio. Puede usar la línea de reinicio para esto, pero esto termina sintetizado como lo menciona The Photon (no suele ser un problema)

Otra forma de hacerlo es establecer el valor cuando codifica el registro, como:

reg [N-1:0]K0 = 8'b00000000;
reg [N-1:0]K1 = 8'b00000000;
reg [N-1:0]K2 = 8'b00000000;

Dependiendo de sus herramientas, esto puede funcionar o no (yo uso las herramientas de Xilinx), y en realidad puede terminar siendo sintetizado (es decir, los registros se configuran durante la carga del flujo de bits en el FPGA), pero pruébelo y háganoslo saber.

Mi memoria es que alguna versión de las herramientas de Xilinx que usé una vez no respetaría esto en síntesis. Por eso, habitualmente no lo codifico de esta manera. Pero creo que es bueno en las herramientas recientes.
Sí, creo que tiene la opción de sintetizar ahora. En cualquier caso, solo lo uso para simulación cuando no tengo una línea de reinicio (y no me importa si las herramientas de síntesis lo respetan o no, como en en la mayoría de los casos, no le importa en qué comienza el registro)