Ich habe einen Eingang, der DDR-Daten bereitstellt, ich muss ihn erfassen und vom FPGA in einem Register (12 Bit) ausgeben.
Wie kann ich es tun?
Was ich jetzt getan habe, ist, die Eingangsdaten mit Always-Block in der steigenden Flanke zu erfassen und in reg1 zu speichern. Erfassen Sie die Eingangsdaten mit einem anderen Always-Block auf der fallenden Flanke und speichern Sie sie in reg2.
Jetzt habe ich Daten der steigenden Flanke auf reg1 [12bit] und der fallenden Flanke auf reg2 [12bit], wie kann ich diese Daten jetzt an ein anderes Ausgangsregister von 12 Bit senden?
mein Code:
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
vorausgesetzt, dass eine Verdoppelung der clk-Frequenz mit PLL keine Option ist. Ich verwende Lattice ECP3 FPGA. Wenn Sie mir mit Lattice IPs helfen können und wie man sie verbindet, würde das auch helfen. Aber ich denke, es gibt einen möglichen Weg ohne IPs, nein?
Danke.
Bearbeiten:
wenn ich data_out auf 24 Bit ändere und diese Codezeile hinzufüge:
assign data_out = {posedge_data,negedge_data};
ist es gutes DDR-Design? oder gibt es einen besseren Weg?
Das normale Verfahren besteht darin, die doppelte Datenbreite innerhalb eines DDR-Empfängers zu verwenden.
Bei einem 12-Bit-DDR-Bus müssen Sie die Daten also an einen 24-Bit breiten Port/Register ausgeben.
Die Alternative besteht darin, die doppelte Taktfrequenz (oder höher) im Inneren zu verwenden, aber wie Sie sagten, ist dies oft keine Option.
Sie würden die Daten NICHT weiterhin intern an abwechselnden Taktflanken verarbeiten. Das ist schlechtes Design.
Ich habe Ihren Code geändert und Reset-Synchronizer und normale Synchronizer hinzugefügt, falls Sie sie benötigen, können Sie sie auch hinzufügen. Von Ihren Port-Verbindungen wurde angenommen, dass der 12-Bit-Eingangsdatenbus mit DDR-Rate vorhanden ist, sodass die Ausgabe 12 * 2 = 24 Bit beträgt und Sie benötigen um es linear zu übertragen, dh data_out [1:0] entsprechend den Daten der fallenden Flanke bzw. den Daten der steigenden Flanke.
Sie können Lattice-Standardmakros IDDR * für diesen Fall sowieso verwenden. Sie können auch umcodieren. Ich habe im System Verilog codiert, falls Ihr Tool den SV nicht unterstützt, verwenden Sie bitte die Verilog-Datentypen im folgenden Code. Wie ich gearbeitet habe In enger Zusammenarbeit mit xilinx-Tools habe ich einige Tool-Direktiven wie (* Dont_touch und ASYNC_REG *) für Synchronisierungen verwendet, um sie für Metastabilitätsbedingungen sehr nützlich zu platzieren. Bitte verwenden Sie solche Direktiven in Ihrem Code, indem Sie sie gemäß den Lattice-Tool-Direktiven ändern.
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
Elliot Alderson
Michael Astahov
Tom Tischler
Michael Astahov