0
\$\begingroup\$

I have an Input which provides DDR data, I need to capture it and output it from the FPGA in one register (12bit).

How can I do it?

What I did for now is to capture the input data with always block in the rising edge and store in reg1. capture the input data with another always block on the falling edge and store in reg2.

Now i have data of the rising edge on reg1[12bit] and falling edge on reg2[12bit], how can I send this data now to another output register of 12 bit?

my 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

assuming that doubling the clk frequency with PLL is not an option. Im using Lattice ECP3 FPGA. If you can help me with Lattice IPs and how to connect them that also would help.. but I think there is possible way without IPs no?

thanks.

Edit:

after Oldfart comment

if I change data_out to 24 bits and add this line of code:

assign data_out = {posedge_data,negedge_data};

does it make it good DDR design? or there is a better way?

\$\endgroup\$
  • \$\begingroup\$ It's not clear to me what you are trying to accomplish. Suppose at some instant in time posedge_data = 12'h123 and negedge_data=12'hABC. What 12-bit data value would you want as an output? What clock edge should cause the output data to be latched? \$\endgroup\$ – Elliot Alderson Feb 24 at 17:17
  • \$\begingroup\$ my goal is to output the data in posedge_data and neg_edge data into output in falling edge and rising edge of the clock.. make the data DDR. it means that in 1 clock cycle I will see the negedge_data and posedge_data in the output register \$\endgroup\$ – Michael Astahov Feb 24 at 18:39
  • \$\begingroup\$ The EPC3 has dedicated DDR blocks on its inputs for this very purpose \$\endgroup\$ – Tom Carpenter Feb 24 at 19:36
  • \$\begingroup\$ @TomCarpenter Hi thanks for the comment, I tried using the DDR, more specific I trying to use: GDDRX1_RX.SCLK.PLL.Aligned that I created from IPexpress on Diamond -> DDR_GENERIC -> and specified my needs. When Im adding the verilog file to the project and add the Instantation to my Top level the project syntisized but after that REVEAL thinks that the ddr IP is my top level design! and I cant add signals to REVEAL from my Top level file, when Im changing top level to my top file its making problems. Do you know why? \$\endgroup\$ – Michael Astahov Feb 24 at 21:22
4
\$\begingroup\$

The normal procedure is to use double the data width inside a DDR receiver.

So with a 12-bit DDR bus you must output the data to a 24 bit wide port/register.

The alternative is to use double the clock frequency (or higher) inside but as you said that is often not an option.

You would NOT keep processing the data internally on alternate clock edges. That is bad design.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ I cant transfer the neg/pos edge data from the 2 registera to 24 bit register? Or I should use DDR reciever inside the FPGA? \$\endgroup\$ – Michael Astahov Feb 24 at 18:19
  • \$\begingroup\$ look at my Edit. thanks \$\endgroup\$ – Michael Astahov Feb 24 at 18:58
  • 1
    \$\begingroup\$ That is the way to do it. The negedge arriving data has only half a clock edge to the next register so normally you would try NOT to add any logic in that stage. \$\endgroup\$ – Oldfart Feb 24 at 19:06
1
\$\begingroup\$

I have modified ur code and added reset synchronizers and normal synchronizers incase if u need u can add them too.. from ur port connections assumed its 12 bit input data bus with DDR rate so output will be 12*2=24 bits and u need to transmit it linear manner that is data_out [1:0] corresponding to falling edge data and rising edge data respectively..

u can use Lattice default macros IDDR* for this case anyways u can recode also , i have coded in system verilog in case if ur tool doesn't support the SV please use verilog data types respectively in the below code.. As i have worked closely with xilinx tools i have used some tool directives like (* Dont_touch and ASYNC_REG*) for synchronizes to place them closely useful for meta-stability condition.. please use such directives in ur code by modifying them according to the lattice tool directives..

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
|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.