FPGA原语及IDDR仿真 Vivado Design Suite 7 Series FPGA and Zynq 7000 SoC Libraries Guide (UG953)

一般来说,在进行HDL代码编写时,不需要直接或间接地进行原语调用,因为随着FPGA设计规模越来越庞大,人脑应该集中于抽象层次较高的工作中去,而将这些具体实现细节交给编译器来完成。不过有些时候,原语或者库中底层模块的调用还是十分必要的。

相关文档如下:

ug471_7Series_SelectIO.pdf at master · Radiumlrb/cnblogs_documents (github.com)

ug768_7series_hdl.pdf at master · Radiumlrb/cnblogs_documents (github.com)

ug953_xilinx14_7-7series_hdl.pdf at master · Radiumlrb/cnblogs_documents

Vivado Design Suite 7 Series FPGA and Zynq 7000 SoC Libraries Guide (UG953)

使用这些原语有以下几种方法:

  1. Instantiation(直接实例化):直接在代码中调用原语,适用于需要精确控制硬件实现的情况。

  2. Inference(推断):通过编写可综合的HDL代码让综合工具自动推断出对应的原语。这种方法具有更好的可移植性和优化空间。

  3. IP Catalog(IP核库):使用Vivado IP Catalog中的预定义IP模块,快速集成复杂功能。

  4. Macro Support(宏支持):利用Xilinx Parameterized Macros(XPM),简化复杂原语的使用,并由工具自动展开为底层原语。

例如,在Xilinx的ISE工具中,可以通过edit→Language Templates菜单调出语法模板,在其中的VHDL或Verilog子菜单下,都可以看到一个Device Primitive Instantiation子项,里面列举出了所有Xilinx FPGA平台上开发项目时可能会用到的原语例化方式。举例简介如下:

1.时钟相关原语

如果时钟信号不是由FPGA芯片的专用时钟pin(或pad)引入FPGA的,那么它通常就需要在FPGA内部被显式地连接到时钟树资源上,否则,直接使用这种不经过时钟树的时钟信号,会给FPGA设计的时序带来非常麻烦的问题,进而导致逻辑行为失败。

BUFG is a high-fanout buffer that connects signals to the global routing resources for low skew distribution of the signal. BUFGs are typically used on clock nets as well other high fanout nets like sets/resets and clock enables.

可是HDL代码仅仅描述功能,无法向编译器表达“希望将某一时钟信号连接到时钟树资源”这样的一层意思,那么此时,就需要使用类似BUFG这样的库里提供的底层模块来进行指示。例如:

--VHDL example
SIGNAL innerclk, gclk : STD_LOGIC;

onToGlobalClockTree : BUFG
PORT MAP(
    I => innerclk, --in std_logic
    O => gclk --out std_logic
);

PROCESS (gclk)
BEGIN
    ......
END PROCESS;
//Verilog example
wire innerclk,gclk;
BUFG onToGlobalClockTree(
    .I (innerclk),
    .O (gclk)
);
always@(posedge gclk)
begin
    ......
end

那么,像上例这样,通过显式调用BUFG这样一个库中的底层模块,就可以告诉编译器,我们希望将innerclk信号引人全局时钟树,而其经过全局时钟树后的名字就改为gclk。后续HDL代码便可以放心地基于gclk编写逻辑。需要说明的是,直接从全局时钟pin(或pad)引入的时钟信号,对于xilinx的FPGA芯片来说,实际上是通过IBUFG+BUFG这样的组合直接连接到全局时钟树上的,只不过此时不需要显式例化这两个原语。
类似的时钟相关原语还有BUFR、BUFIO等,它们分别对应区域时钟树和IO时钟树资源,这里就不再赘述。

2.差分输入、输出原语

FPGA的接口具有单端和差分两种形式,同样,HDL代码只能描述功能,无法表达“某两个pin(或pad)脚互为一个差分对”这样的一层意思。那么此时,就需要使用类似IBUFDS、IBUFGDS、OBUFDS这样的库里提供的底层模块或原语来进行指示。例如:

--VHDL example
clklvds : IBUFGDS
GENERIC MAP(
    DIFF_TERM => TRUE,
    IBUF_DELAY_VALUE => "0",
    IOSTANDARD => "DEFAULT")
PORT MAP(
    I = LVDSCIk_p,
    IB => LVDSclk n,
    O => sclk);
onToGlobalClockTree : BUFG
PORT MAP(
    I => sclk, --in std logic
    O => gclk --out std logic
);
//Verilog example
IBUFGDS clklvds(
    .I (LVDSClk_p),
    .IB (LVDSClk_n),
    .O (sclk)
);
BUFG onToGlobalClockTree(
    .I (sclk),
    .O(gclk)
);

上述代码通过调用原语IBUFGDS,表达了“LVDSCIk_p、LVDSCIk_n是一对差分时钟输入信号且它们是经由FPGA芯片的专用差分时钟输人管脚引入FPGA芯片”的意思,这样编译器就会采用接口资源中的专用差分时钟输入电路来接入这对时钟输入信号,并转换成为一个单端时钟sclk,紧接着,在通过一个BUFG原语将其引至全局时钟网络,这样全局时钟网络输出的gclk就可以作为后续内部逻辑的时钟源。

4.跨时钟域同步原语

在数字系统中,当信号需要从一个时钟域跨越到另一个不同时钟域时,可能会由于时序不匹配而引发亚稳态问题。为了避免这种不确定性,通常需要引入同步电路来对跨时钟域的信号进行处理。

在ug953文档中,有几种建议的clock domain crossing原语

  • XPM_CDC_ASYNC_RST:异步复位同步器
  • XPM_CDC_GRAY:通过格雷码编码进行同步
  • XPM_CDC_HANDSHAKE:带全握手的总线同步器
  • XPM_CDC_PULSE:脉冲信号跨时钟域传输
  • XPM_CDC_SINGLE:单比特信号同步器
  • XPM_CDC_SYNC_RST:同步复位同步器

Xilinx 提供了基于 XPM(Xilinx Parameterized Macro)库的一系列 CDC原语。

(详见cnblogs_documents/ug953_xilinx14_7-7series_hdl.pdf at master · Radiumlrb/cnblogs_documents)

使用方式


-- xpm_cdc_array_single: Single-bit Array Synchronizer
-- Xilinx Parameterized Macro, version 2025.1

xpm_cdc_array_single_inst : xpm_cdc_array_single
generic map (
   DEST_SYNC_FF => 4,   -- DECIMAL; range: 2-10
   INIT_SYNC_FF => 0,   -- DECIMAL; 0=disable simulation init values, 1=enable simulation init values
   SIM_ASSERT_CHK => 0, -- DECIMAL; 0=disable simulation messages, 1=enable simulation messages
   SRC_INPUT_REG => 1,  -- DECIMAL; 0=do not register input, 1=register input
   WIDTH => 2           -- DECIMAL; range: 1-1024
)
port map (
   dest_out => dest_out, -- WIDTH-bit output: src_in synchronized to the destination clock domain. This output is registered.
   dest_clk => dest_clk, -- 1-bit input: Clock signal for the destination clock domain.
   src_clk => src_clk,   -- 1-bit input: optional; required when SRC_INPUT_REG = 1
   src_in => src_in      -- WIDTH-bit input: Input single-bit array to be synchronized to destination clock domain. It is assumed that each bit of
                         -- the array is unrelated to the others. This is reflected in the constraints applied to this macro. To transfer a binary
                         -- value losslessly across the two clock domains, use the XPM_CDC_GRAY macro instead.

);

-- End of xpm_cdc_array_single_inst instantiation

2. FIFO

用于数据缓冲和跨时钟域的数据传输。

  • XPM_FIFO_ASYNC:异步FIFO
  • XPM_FIFO_AXIF:AXI-Full FIFO
  • XPM_FIFO_AXIL:AXI-Lite FIFO
  • XPM_FIFO_AXIS:AXI Stream FIFO
  • XPM_FIFO_SYNC:同步FIFO
-- xpm_fifo_async: Asynchronous FIFO
-- Xilinx Parameterized Macro, version 2025.1

xpm_fifo_async_inst : xpm_fifo_async
generic map (
   CASCADE_HEIGHT => 0,            -- DECIMAL
   CDC_SYNC_STAGES => 2,           -- DECIMAL
   DOUT_RESET_VALUE => "0",        -- String
   ECC_MODE => "no_ecc",           -- String
   EN_SIM_ASSERT_ERR => "warning", -- String
   FIFO_MEMORY_TYPE => "auto",     -- String
   FIFO_READ_LATENCY => 1,         -- DECIMAL
   FIFO_WRITE_DEPTH => 2048,       -- DECIMAL
   FULL_RESET_VALUE => 0,          -- DECIMAL
   PROG_EMPTY_THRESH => 10,        -- DECIMAL
   PROG_FULL_THRESH => 10,         -- DECIMAL
   RD_DATA_COUNT_WIDTH => 1,       -- DECIMAL
   READ_DATA_WIDTH => 32,          -- DECIMAL
   READ_MODE => "std",             -- String
   RELATED_CLOCKS => 0,            -- DECIMAL
   SIM_ASSERT_CHK => 0,            -- DECIMAL; 0=disable simulation messages, 1=enable simulation messages
   USE_ADV_FEATURES => "0707",     -- String
   WAKEUP_TIME => 0,               -- DECIMAL
   WRITE_DATA_WIDTH => 32,         -- DECIMAL
   WR_DATA_COUNT_WIDTH => 1        -- DECIMAL
)
port map (
   almost_empty => almost_empty,   -- 1-bit output: Almost Empty : When asserted, this signal indicates that only one more read can be performed
                                   -- before the FIFO goes to empty.

   almost_full => almost_full,     -- 1-bit output: Almost Full: When asserted, this signal indicates that only one more write can be performed
                                   -- before the FIFO is full.

   data_valid => data_valid,       -- 1-bit output: Read Data Valid: When asserted, this signal indicates that valid data is available on the
                                   -- output bus (dout).

   dbiterr => dbiterr,             -- 1-bit output: Double Bit Error: Indicates that the ECC decoder detected a double-bit error and data in the
                                   -- FIFO core is corrupted.

   dout => dout,                   -- READ_DATA_WIDTH-bit output: Read Data: The output data bus is driven when reading the FIFO.
   empty => empty,                 -- 1-bit output: Empty Flag: When asserted, this signal indicates that the FIFO is empty. Read requests are
                                   -- ignored when the FIFO is empty, initiating a read while empty is not destructive to the FIFO.

   full => full,                   -- 1-bit output: Full Flag: When asserted, this signal indicates that the FIFO is full. Write requests are
                                   -- ignored when the FIFO is full, initiating a write when the FIFO is full is not destructive to the contents
                                   -- of the FIFO.

   overflow => overflow,           -- 1-bit output: Overflow: This signal indicates that a write request (wren) during the prior clock cycle was
                                   -- rejected, because the FIFO is full. Overflowing the FIFO is not destructive to the contents of the FIFO.

   prog_empty => prog_empty,       -- 1-bit output: Programmable Empty: This signal is asserted when the number of words in the FIFO is less than
                                   -- or equal to the programmable empty threshold value. It is de-asserted when the number of words in the FIFO
                                   -- exceeds the programmable empty threshold value.

   prog_full => prog_full,         -- 1-bit output: Programmable Full: This signal is asserted when the number of words in the FIFO is greater
                                   -- than or equal to the programmable full threshold value. It is de-asserted when the number of words in the
                                   -- FIFO is less than the programmable full threshold value.

   rd_data_count => rd_data_count, -- RD_DATA_COUNT_WIDTH-bit output: Read Data Count: This bus indicates the number of words read from the FIFO.
   rd_rst_busy => rd_rst_busy,     -- 1-bit output: Read Reset Busy: Active-High indicator that the FIFO read domain is currently in a reset state.
   sbiterr => sbiterr,             -- 1-bit output: Single Bit Error: Indicates that the ECC decoder detected and fixed a single-bit error.
   underflow => underflow,         -- 1-bit output: Underflow: Indicates that the read request (rd_en) during the previous clock cycle was
                                   -- rejected because the FIFO is empty. Under flowing the FIFO is not destructive to the FIFO.

   wr_ack => wr_ack,               -- 1-bit output: Write Acknowledge: This signal indicates that a write request (wr_en) during the prior clock
                                   -- cycle is succeeded.

   wr_data_count => wr_data_count, -- WR_DATA_COUNT_WIDTH-bit output: Write Data Count: This bus indicates the number of words written into the
                                   -- FIFO.

   wr_rst_busy => wr_rst_busy,     -- 1-bit output: Write Reset Busy: Active-High indicator that the FIFO write domain is currently in a reset
                                   -- state.

   din => din,                     -- WRITE_DATA_WIDTH-bit input: Write Data: The input data bus used when writing the FIFO.
   injectdbiterr => injectdbiterr, -- 1-bit input: Double Bit Error Injection: Injects a double bit error if the ECC feature is used on block RAMs
                                   -- or UltraRAM macros.

   injectsbiterr => injectsbiterr, -- 1-bit input: Single Bit Error Injection: Injects a single bit error if the ECC feature is used on block RAMs
                                   -- or UltraRAM macros.

   rd_clk => rd_clk,               -- 1-bit input: Read clock: Used for read operation. rd_clk must be a free running clock.
   rd_en => rd_en,                 -- 1-bit input: Read Enable: If the FIFO is not empty, asserting this signal causes data (on dout) to be read
                                   -- from the FIFO. Must be held active-low when rd_rst_busy is active high.

   rst => rst,                     -- 1-bit input: Reset: Must be synchronous to wr_clk. The clock(s) can be unstable at the time of applying
                                   -- reset, but reset must be released only after the clock(s) is/are stable.

   sleep => sleep,                 -- 1-bit input: Dynamic power saving: If sleep is High, the memory/fifo block is in power saving mode.
   wr_clk => wr_clk,               -- 1-bit input: Write clock: Used for write operation. wr_clk must be a free running clock.
   wr_en => wr_en                  -- 1-bit input: Write Enable: If the FIFO is not full, asserting this signal causes data (on din) to be written
                                   -- to the FIFO. Must be held active-low when rst or wr_rst_busy is active high.

);

-- End of xpm_fifo_async_inst instantiation

3. Memory (通常使用ip核调用)

实现各种类型的存储器,如RAM、ROM等。

  • XPM_MEMORY_DPDISTRAM:双端口分布式RAM
  • XPM_MEMORY_DPROM:双端口ROM
  • XPM_MEMORY_SDPRAM:简易双端口RAM
  • XPM_MEMORY_SPRAM:单端口RAM
  • XPM_MEMORY_SPROM:单端口ROM
  • XPM_MEMORY_TDPRAM:真双端口RAM(True Dual Port RAM)
-- xpm_memory_sdpram: Simple Dual Port RAM
-- Xilinx Parameterized Macro, version 2025.1

xpm_memory_sdpram_inst : xpm_memory_sdpram
generic map (
   ADDR_WIDTH_A => 6,               -- DECIMAL
   ADDR_WIDTH_B => 6,               -- DECIMAL
   AUTO_SLEEP_TIME => 0,            -- DECIMAL
   BYTE_WRITE_WIDTH_A => 32,        -- DECIMAL
   CASCADE_HEIGHT => 0,             -- DECIMAL
   CLOCKING_MODE => "common_clock", -- String
   ECC_BIT_RANGE => "7:0",          -- String
   ECC_MODE => "no_ecc",            -- String
   ECC_TYPE => "none",              -- String
   IGNORE_INIT_SYNTH => 0,          -- DECIMAL
   MEMORY_INIT_FILE => "none",      -- String
   MEMORY_INIT_PARAM => "0",        -- String
   MEMORY_OPTIMIZATION => "true",   -- String
   MEMORY_PRIMITIVE => "auto",      -- String
   MEMORY_SIZE => 2048,             -- DECIMAL
   MESSAGE_CONTROL => 0,            -- DECIMAL
   RAM_DECOMP => "auto",            -- String
   READ_DATA_WIDTH_B => 32,         -- DECIMAL
   READ_LATENCY_B => 2,             -- DECIMAL
   READ_RESET_VALUE_B => "0",       -- String
   RST_MODE_A => "SYNC",            -- String
   RST_MODE_B => "SYNC",            -- String
   SIM_ASSERT_CHK => 0,             -- DECIMAL; 0=disable simulation messages, 1=enable simulation messages
   USE_EMBEDDED_CONSTRAINT => 0,    -- DECIMAL
   USE_MEM_INIT => 1,               -- DECIMAL
   USE_MEM_INIT_MMI => 0,           -- DECIMAL
   WAKEUP_TIME => "disable_sleep",  -- String
   WRITE_DATA_WIDTH_A => 32,        -- DECIMAL
   WRITE_MODE_B => "no_change",     -- String
   WRITE_PROTECT => 1               -- DECIMAL
)
port map (
   dbiterrb => dbiterrb,             -- 1-bit output: Status signal to indicate double bit error occurrence on the data output of port B.
   doutb => doutb,                   -- READ_DATA_WIDTH_B-bit output: Data output for port B read operations.
   sbiterrb => sbiterrb,             -- 1-bit output: Status signal to indicate single bit error occurrence on the data output of port B.
   addra => addra,                   -- ADDR_WIDTH_A-bit input: Address for port A write operations.
   addrb => addrb,                   -- ADDR_WIDTH_B-bit input: Address for port B read operations.
   clka => clka,                     -- 1-bit input: Clock signal for port A. Also clocks port B when parameter CLOCKING_MODE is "common_clock".
   clkb => clkb,                     -- 1-bit input: Clock signal for port B when parameter CLOCKING_MODE is "independent_clock". Unused when
                                     -- parameter CLOCKING_MODE is "common_clock".

   dina => dina,                     -- WRITE_DATA_WIDTH_A-bit input: Data input for port A write operations.
   ena => ena,                       -- 1-bit input: Memory enable signal for port A. Must be high on clock cycles when write operations are
                                     -- initiated. Pipelined internally.

   enb => enb,                       -- 1-bit input: Memory enable signal for port B. Must be high on clock cycles when read operations are
                                     -- initiated. Pipelined internally.

   injectdbiterra => injectdbiterra, -- 1-bit input: Controls double bit error injection on input data when ECC enabled (Error injection
                                     -- capability is not available in "decode_only" mode).

   injectsbiterra => injectsbiterra, -- 1-bit input: Controls single bit error injection on input data when ECC enabled (Error injection
                                     -- capability is not available in "decode_only" mode).

   regceb => regceb,                 -- 1-bit input: Clock Enable for the last register stage on the output data path.
   rstb => rstb,                     -- 1-bit input: Reset signal for the final port B output register stage. Synchronously resets output port
                                     -- doutb to the value specified by parameter READ_RESET_VALUE_B.

   sleep => sleep,                   -- 1-bit input: sleep signal to enable the dynamic power saving feature.
   wea => wea                        -- WRITE_DATA_WIDTH_A/BYTE_WRITE_WIDTH_A-bit input: Write enable vector for port A input data port dina. 1
                                     -- bit wide when word-wide writes are used. In byte-wide write configurations, each bit controls the writing
                                     -- one byte of dina to address addra. For example, to synchronously write only bits [15-8] of dina when
                                     -- WRITE_DATA_WIDTH_A is 32, wea would be 4'b0010.

);

-- End of xpm_memory_sdpram_inst instantiation

4.接口相关原语

当需要使用接口资源中的寄存器来实现高速数据采集时,除了使用寄存器约束外,如果直接调用相关的原语,编译器便会利用接口资源中的寄存器来做事情。例如,使用ODDR、IDDR、IDDR2CLK、ISERDES、OSERDES等原语后,编译器便会利用接口资源的寄存器来实现相关接口功能,这样便能达到比较高的性能。例如:

--VHDL example
isO:ISERDES_NODELAY
generic map (
    BITSLIP_ENABLE => TRUE, --TRUE/FALSE to enable bitslip controller
    DATA_RATE => "DDR", --Specify data rate of "DDR" or "SDR"
    DATA_WIDTH => 6, --Specify data width
    --NETWORKING SDR:2,3,4,5,6,7,8:DDR 4,6,8,10
    --MEMORY SDR N/A:DDR 4
    INTERFACE_TYPE => NETWORKING, --Use model "MEMORY" or "NETWORKING"
    NUM_CE => 2, --Define number of clock enables to an integer of 1 or 2
    SERDES_MODE => "MASTER" --Set SERDES mode to "MASTER" or "SLAVE"
)
port map (
    Q1 => dataOLine(0), --1-bit registered SERDES output
    Q2 => dataOLine(1), --1-bit registered SERDES output
    Q3 => dataOLine(2), --1-bit registered SERDES output
    Q4 => dataOLine(3), --1-bit registered SERDES output
    Q5 => dataOLine(4), --1-bit registered SERDES output
    Q6 => dataOLine(5), --1-bit registered SERDES output
    SHIFTOUT1 => open, --1-bit cascade Master/Slave output
    SHIFTOUT2 => open, --1-bit cascade Master/Slave output
    BITSLIP => '0', --1-bit Bitslip enable input
    CE1 => '1', --1-bit clock enable input
    CE2 => '1', --1-bit clock enable input
    CLK => clkFast, --1-bit master clock input
    CLKB => clkFastInvert, --1-bit secondary clock input for DATA RATE DDR
    CLKDIV => clkSlow, --1-bit divided clock input
    D => din, --1-bit data input,connects to IODELAY or input buffer
    0CLK => '0', --1-bit fast output clock input
    RST => rst, --1-bit asynchronous reset input
    SHIFTIN1 => '0', --1-bit cascade Master/Slave input
    SHIFTIN2 => '0' --1-bit cascade Master/Slave input
)
//Verilog example
ISERDES_NODELAY #(
    .BITSLIP_ENABLE("FALSE"), // "TRUE" or "FALSE" to enable bitslip controller
    // Must be "FALSE" if INTERFACE_TYPE set to "MEMORY"
    .DATA_RATE("DDR"), // Specify data rate of "DDR" or "SDR"
    .DATA_WIDTH(4), // Specify data width
    // NETWORKING SDR: 2, 3, 4, 5, 6, 7, 8; DDR: 4, 6, 8, 10
    // MEMORY SDR: N/A; DDR: 4
    .INTERFACE_TYPE("MEMORY"), // Use model - "MEMORY" or "NETWORKING"
    .NUM_CE(2), // Number of clock enables used, 1 or 2
    .SERDES_MODE(MASTER) // Set SERDES mode to "MASTER" or "SLAVE" 
    .Q1(dataOLine[0]), // 1-bit registered SERDES output
    .Q2(dataOLine[1]), // 1-bit registered SERDES output
    .Q3(dataOLine[2]), // 1-bit registered SERDES output
    .Q4(dataOLine[3]), // 1-bit registered SERDES output
    .Q5(dataOLine[4]), // 1-bit registered SERDES output
    .Q6(dataOLine[5]), // 1-bit registered SERDES output
    .SHIFTOUT1(open), // 1-bit cascade Master/Slave output
    .SHIFTOUT2(open), // 1-bit cascade Master/Slave output
    .BITSLIP(1'b0), // 1-bit Bitslip enable input
    .CE1(1'b1), // 1-bit clock enable input
    .CE2(1'b1), // 1-bit clock enable input
    .CLK(clkFast), // 1-bit master clock input
    .CLKB(clkFastInvert), // 1-bit secondary clock input for DATA_RATE DDR
    .CLKDIV(clkSlow), // 1-bit divided clock input
    .D(din), // 1-bit data input, connects to IODELAY or input buffer
    .IOCLK(1'b0), // 1-bit fast output clock input
    .RST(rst), // 1-bit asynchronous reset input
    .SHIFTIN1(1'b0), // 1-bit cascade Master/Slave input
    .SHIFTIN2(1'b0) // 1-bit cascade Master/Slave input
);

使用上述原语后,编译器便会利用接口资源中的寄存器实现一个针对输入接口的高性能1:6串并转换器。当然了,为了保证串并处理的成功,仅仅使用ISERDES原语还远远不够,时钟信号也必须按照要求来处理,具体的要求可以查阅Xilinx公司相应器件的用户手册。

 

ODDR双边沿转单边沿

某些AD芯片通信数据接口采用了DDR双沿采样模式,如AD9361/AD9653与Xilinx FPGA通信需要用到其IDDR和ODDR原语。

ODDR是将2bit单沿采样输入转换为1bit的双沿采样输出,ODDR框图如下图所示,其中D1和D1为单沿采样输入端,C为时钟输入,CE为时钟使能,S为置位信号,R为复位信号,Q为双沿采样输出端。

两种工作模式如下

实例代码如下

ad1_clk : ODDR
    generic map(
        DDR_CLK_EDGE => "OPPOSITE_EDGE", -- "OPPOSITE_EDGE" or "SAME_EDGE"
        INIT => '0', -- Initial value for Q port ('1' or '0')
        SRTYPE => "SYNC") -- Reset Type ("ASYNC" or "SYNC")
    port map (
        Q => ad9653_1clk, -- 1-bit DDR output
        C => clk18, -- 1-bit clock input
        CE => '1', -- 1-bit clock enable input
        D1 => '1', -- 1-bit data input (positive edge)
        D2 => '0', -- 1-bit data input (negative edge)
        R => '0', -- 1-bit reset input
        S => '0' -- 1-bit set input
    );

 

 

 

IDDR单边沿转双边沿

IDDR的主要功能就是将输入的双沿信号转换为单沿信号输出给FPGA内部逻辑进行使用,IDDR位于通1中的ILOGICE部分,在讲解IDDR使用前,需要了解ILOGICE的结构及功能。

IDDR的功能就是将双沿采样的数据转换为单沿数据传输给FPGA内部进行使用。FPGA内部的D触发器一般都是在时钟上升沿去采集输出数据,这种方式被称为SDR,与SDRAM传输数据类似。在SDRAM之后,为了提高数据传输速率,推出了DDR,在时钟的上升沿和下降沿都能传输数据,同样时钟频率下,速率可以提升一倍,这种传输数据的方式就是双沿传输,这种方式一般都只存在接口部分,内部电路采用双沿会比较麻烦,所以会转换为单沿进行处理,FPGA调用IDDR原语即可实现。

IDDR原语模板

IDDR #(
      .DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE", "SAME_EDGE" 
                                      //    or "SAME_EDGE_PIPELINED" 
      .INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
      .INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
      .SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC" 
   ) IDDR_inst (
      .Q1(Q1), // 1-bit output for positive edge of clock
      .Q2(Q2), // 1-bit output for negative edge of clock
      .C(C),   // 1-bit clock input
      .CE(CE), // 1-bit clock enable input
      .D(D),   // 1-bit DDR data input
      .R(R),   // 1-bit reset
      .S(S)    // 1-bit set
   );

从上面例化的模块说明IDDR的参数,其中INIT_Q1和INIT_Q1为输出初始值,SRTYPE表示同步或异步复位,而DDR_CLK_EDGE有以下三种模式:
IDDR 有三种工作模式,模式配置如下图所示,分别为:OPPOSITE_EDGE、SAME_EDGE 、SAME_EDGE_PIPELINED。

代码与仿真

这里对IDDR进行仿真,模块代码如下

module iddr_ctrl(
   input       clk      ,//系统时钟信号;
   input       rst      ,//系统复位信号,高电平有效;

   input       clk_en   ,//时钟使能信号;
   input       din      ,//输入数据;
   output      dout1    ,//输出数据
   output      dout2    
); 
   reg         clk_en_r ;
   //(* IOB = "TRUE" *)reg clk_en_r ;//将clk_en_r放在ILOGICE中;

   //将clk_en打拍,用于验证IOB原语是否有效;
   always@(posedge clk)begin
      clk_en_r <= clk_en;
   end

   //例化IDDR原语
   IDDR #(
      .DDR_CLK_EDGE  ("SAME_EDGE_PIPELINED"  ),// "OPPOSITE_EDGE", "SAME_EDGE" or "SAME_EDGE_PIPELINED" 
      .INIT_Q1       (1'b0                   ),// Initial value of Q1: 1'b0 or 1'b1
      .INIT_Q2       (1'b0                   ),// Initial value of Q2: 1'b0 or 1'b1
      .SRTYPE        ("SYNC"                 ) // Set/Reset type: "SYNC" or "ASYNC" 
   ) 
   IDDR_inst (
      .Q1   (dout1   ),// 1-bit output for positive edge of clock
      .Q2   (dout2   ),// 1-bit output for negative edge of clock
      .C    (clk     ),// 1-bit clock input
      .CE   (clk_en_r),// 1-bit clock enable input
      .D    (din     ),// 1-bit DDR data input
      .R    (rst     ),// 1-bit reset
      .S    (1'b0    ) // 1-bit set
   );

endmodule

testbench

`timescale 1 ns/1 ns
module test();
    parameter  CYCLE    =   10      ;//系统时钟周期,单位ns,默认10ns;
    parameter  RST_TIME  =   10      ;//系统复位持续时间,默认10个系统时钟周期;

    reg                      clk     ;//系统时钟,默认100MHz;
    reg                      rst     ;//系统复位,默认高电平有效;
    reg                         clk_en  ;
    reg                         din     ;

    wire                        dout1   ;
    wire                        dout2   ;

    iddr_ctrl  u_iddr_ctrl (
        .clk    ( clk       ),
        .rst    ( rst       ),
        .clk_en ( clk_en    ),
        .din    ( din       ),
        .dout1  ( dout1     ),
        .dout2  ( dout2     )
    );

    //生成周期为CYCLE数值的系统时钟;
    initial begin
        clk = 1;
        forever #(CYCLE/2) clk = ~clk;
    end

    //生成复位信号;
    initial begin
        rst = 0;
        #2;
        rst = 1;//开始时复位10个时钟;
        #(RST_TIME*CYCLE);
        rst = 0;
        repeat(120) @(posedge clk);
        $stop;//停止仿真;
    end

    initial begin
        #1;
        din = 1'b0;clk_en = 1'b0;
        #(CYCLE*(10+RST_TIME));
        clk_en = 1'b1;
        #(CYCLE);
        repeat(100)begin//产生100个双沿时钟数据。
            #(CYCLE/2);
            din = ({$random} % 2);
            #(CYCLE/2);
            din = ({$random} % 2);
        end
        #(CYCLE);
        clk_en = 1'b0;
    end


endmodule

仿真结果如图,可以看到时钟的上边沿采样为dout1,下边沿采样为dout2

posted @ 2024-03-27 20:20  Radiumlrb  阅读(1175)  评论(0)    收藏  举报