Tengo una entrada que proporciona datos DDR, necesito capturarlos y enviarlos desde el FPGA en un registro (12 bits).
¿Cómo puedo hacerlo?
Lo que hice por ahora es capturar los datos de entrada con bloque siempre en el borde ascendente y almacenarlos en reg1. capture los datos de entrada con otro bloque siempre en el borde descendente y guárdelos en reg2.
Ahora tengo datos del flanco ascendente en reg1 [12 bits] y el flanco descendente en reg2 [12 bits], ¿cómo puedo enviar estos datos ahora a otro registro de salida de 12 bits?
mi código:
module ADC(
input rstn,
input clk,
input [11:0] data_in,
output [11:0] data_out
);
reg rst;
reg [11:0] posedge_data;
reg [11:0] negedge_data;
always @(posedge clk or negedge rstn)
begin
rst <= ~rstn;
end
always @(posedge clk, posedge rst)
begin
if (rst) begin
posedge_data <= 12'b100000000000;
end else begin
posedge_data <= data_in;
end
end
always @(negedge clk, posedge rst)
begin
if (rst) begin
negedge_data <= 12'b100000000000;
end else begin
negedge_data <= data_in;
end
end
endmodule
asumiendo que duplicar la frecuencia clk con PLL no es una opción. Estoy usando Lattice ECP3 FPGA. Si me pueden ayudar con las IP de Lattice y cómo conectarlas, eso también ayudaría... pero creo que hay una forma posible sin IP, ¿no?
gracias.
Editar:
si cambio data_out a 24 bits y agrego esta línea de código:
assign data_out = {posedge_data,negedge_data};
¿Lo hace un buen diseño DDR? o hay una mejor manera?
El procedimiento normal es utilizar el doble de ancho de datos dentro de un receptor DDR.
Entonces, con un bus DDR de 12 bits, debe enviar los datos a un puerto/registro de 24 bits de ancho.
La alternativa es usar el doble de la frecuencia del reloj (o más) en el interior, pero como dijiste, a menudo no es una opción.
NO seguiría procesando los datos internamente en flancos de reloj alternativos. Eso es mal diseño.
Modifiqué su código y agregué sincronizadores de reinicio y sincronizadores normales en caso de que los necesite, también puede agregarlos ... desde las conexiones de su puerto asumió que su bus de datos de entrada de 12 bits con velocidad DDR, por lo que la salida será 12 * 2 = 24 bits y necesita para transmitirlo de manera lineal, es decir, data_out [1:0] correspondiente a los datos de flanco descendente y los datos de flanco ascendente, respectivamente.
puede usar las macros predeterminadas de Lattice IDDR * para este caso de todos modos, también puede recodificar, he codificado en el sistema verilog en caso de que su herramienta no sea compatible con el SV, use los tipos de datos de verilog respectivamente en el código a continuación. Como he trabajado estrechamente con las herramientas xilinx, he usado algunas directivas de herramientas como (* Dont_touch y ASYNC_REG*) para sincronizaciones para colocarlas muy útiles para la condición de metaestabilidad. Utilice dichas directivas en su código modificándolas de acuerdo con las directivas de la herramienta de celosía.
module ADC(
input rstn,
input clk,
input [11:0] data_in,
output [23:0] data_out
);
reg rst;
reg [11:0] posedge_data;
reg [11:0] negedge_data;
always @(posedge clk or negedge rstn) // expecting reset is synchronous to this clock domain if not use
rst <= ~rstn; // reset synchronizer in ur code to remove metastability during reset removal and recovery
always @(posedge clk, posedge rst)
if (rst) posedge_data <= 12'b1000_0000_0000;
else posedge_data <= data_in; // Assuming data_in in synchronous to the design else u need to atleast 2-FF stage synchronizer
always @(negedge clk, posedge rst)
if (rst) negedge_data <= 12'b100000000000;
else negedge_data <= data_in;// Assuming data_in in synchronous to the design else u need to atleast 2-FF stage synchronizer
for (int i=0;i<12;i++)
assign data_out[i+:2]={negedge_data[i],posedge_data[i]};
endmodule
module reset_sync #(
parameter TCQ = 100,
parameter NUM_OF_STAGES = 2,
parameter IN_ACTIVE_HIGH_RESET = 1,
parameter OUT_ACTIVE_HIGH_RESET = 1
)(
input dest_clk, async_rst,
output logic sync_rst_dest_clk
output logic async_rst_out;
);
(* DONT_TOUCH = "TRUE" , ASYNC_REG = "TRUE" *) logic [NUM_OF_STAGES-1:0] sync = 0;
logic sync_rst_out;
reg rst_out = 0;
if(IN_ACTIVE_HIGH_RESET)begin: ASYNC_RESET
always_ff @(posedge dest_clk or posedge async_rst)
if (async_rst) sync <= #TCQ OUT_ACTIVE_HIGH_RESET ? '1 : '0;
else sync <= #TCQ OUT_ACTIVE_HIGH_RESET ? {sync[NUM_OF_STAGES-2:0],1'b0}
: {sync[NUM_OF_STAGES-2:0],1'b1} ;
end else begin: ASYNC_RESET
always_ff @(posedge dest_clk or negedge async_rst)
if(~async_rst) sync <= #TCQ OUT_ACTIVE_HIGH_RESET ? '1 : '0;
else sync <= #TCQ OUT_ACTIVE_HIGH_RESET ? {sync[NUM_OF_STAGES-2:0],1'b0}
: {sync[NUM_OF_STAGES-2:0],1'b1} ;
end
assign async_rst_out = sync[NUM_OF_STAGES-1];
sync # (
.SYNC_MTBF (2)
,.WIDTH (1)
) u_dest_rst_sync(
.clk (dest_clk)
,.data_in (async_rst_out)
,.data_out (sync_rst_out)
);
assign sync_rst_dest_clk = sync_rst_out;
endmodule
module sync #(
parameter SYNC_MTBF = 2
,parameter WIDTH = 1
,parameter TCQ = 100
)(
input clk
,input [WIDTH-1:0] data_in
,output [WIDTH-1:0] data_out
);
for (genvar wd=0; wd<WIDTH; wd=wd+1) begin : SYNC
(* dont_touch = "true" *) (* ASYNC_REG = "TRUE" *) reg [SYNC_MTBF-1:0] sync_reg;
always @(posedge clk)
sync_reg <= #TCQ {sync_reg[0+:SYNC_MTBF-1], data_in};
assign data_out[wd] = sync_reg[SYNC_MTBF-1];
end
endmodule
eliot alderson
Michael Astahov
tom carpintero
Michael Astahov