So geben Sie DDR-Daten an 1 Register aus

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:

nach Oldfart Kommentar

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?

Mir ist nicht klar, worauf du hinaus willst. Angenommen, zu einem bestimmten Zeitpunkt posedge_data = 12'h123 und negedge_data=12'hABC. Welchen 12-Bit-Datenwert möchten Sie als Ausgabe? Welche Taktflanke soll bewirken, dass die Ausgangsdaten zwischengespeichert werden?
Mein Ziel ist es, die Daten in posedge_data und neg_edge-Daten in die Ausgabe bei fallender Flanke und steigender Flanke des Takts auszugeben. Machen Sie die Daten DDR. es bedeutet, dass ich in 1 Taktzyklus die negedge_data und posedge_data im Ausgangsregister sehen werde
Der EPC3 hat für genau diesen Zweck dedizierte DDR-Blöcke an seinen Eingängen
@TomCarpenter Hallo, danke für den Kommentar, ich habe versucht, die DDR zu verwenden, genauer gesagt, ich habe versucht, sie zu verwenden: GDDRX1_RX.SCLK.PLL.Aligned, das ich von IPexpress auf Diamond erstellt habe -> DDR_GENERIC -> und meine Bedürfnisse angegeben. Wenn ich die Verilog-Datei zum Projekt hinzufüge und die Instanzierung zu meiner obersten Ebene hinzufüge, wurde das Projekt syntisiert, aber danach denkt REVEAL, dass die ddr-IP mein oberstes Level-Design ist! und ich kann REVEAL keine Signale aus meiner Datei der obersten Ebene hinzufügen, wenn ich die oberste Ebene in meine Datei der obersten Ebene ändere, treten Probleme auf. Weißt du, warum?

Antworten (2)

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 kann die Neg/Pos-Flankendaten nicht von den 2 Registern in das 24-Bit-Register übertragen? Oder sollte ich einen DDR-Empfänger im FPGA verwenden?
schau dir meine Bearbeitung an. Danke
Das ist der Weg, es zu tun. Die negativ ankommenden Daten haben nur eine halbe Taktflanke zum nächsten Register, so dass Sie normalerweise versuchen würden, in dieser Phase KEINE Logik hinzuzufügen.

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