概述
导言
上一期主要讲解了一些概念性的东西以及工程的恢复,本期主要简单讲一下用到的原语以及仿真FIFO,先仿真FIFO最主要的原因是FIFO很简单,需要频繁的调用。
3顶层以及原语
3.1项目顶层
上一期我们讲解过了工程的恢复,下图是该rgmii项目综合完的顶层原理图(kintex7),顶层涉及到的模块有,差分时钟buffer,输入延迟,时钟分频倍频(时钟管理MMCM,和PLL相比的优势是可以动态调整相位),同步复位异步释放模块(关于这个知识点请参阅“”一文,这里不多赘述),延迟模块(IDELAYE2),延迟控制模块(IDELAYCTRL),项目核心代码以及一些BUFG(全局时钟网络)。这里主要讲解一下延迟以及IDDR/ODDR(该模块调用放在fpga_core内部,因为要兼容quartus和vivado平台,所以单独放在一个文件里面)。
原理图顶层
3.2 IDELAYE2以及IDELAYCTRL
前面在谈到RGMII时序时讲到,输入数据和时钟有延迟模式,IDELAYE2就是用来延迟的原语(Primitive,属于底层硬件功能单元,各个厂商都不太一样)。延迟的类型有FIXED
, VARIABLE
, VAR_LOAD
, VAR_LOAD_PIPE
,默认为FIXED
, 即延迟的数值固定,当然也有动态设置的模式,一般常用FIXED
模式。值得注意的是REFCLK_FREQUENCY
尽管可以设置为190-210MHz,290-310MHz,390-410MHz,但并不是所有的器件速度等级都支持290-310MHz以及390-410MHz,具体参阅自己的芯片手册。其他参数根据需求使用。
关于IDELAYCTRL
,只要例化了IDELAYE2
/ODELAYE2
,就必须例化IDELAYCTRL
,这个模块主要为了校准IDELAYE2
/ODELAYE2
,注意时钟接口连接入模块必须经由全局时钟buffer,如本项目TOP原理图所示,RDY
可不接。
下面代码分别为IDELAYE2
和IDELAYCTRL
例化模版(引自Vivado2022.1,Kintex7模版)
// IDELAYE2 : In order to incorporate this function into the design,
// Verilog : the following instance declaration needs to be placed
// instance : in the body of the design code. The instance name
// declaration : (IDELAYE2_inst) and/or the port declarations within the
// code : parenthesis may be changed to properly reference and
// : connect this function to the design. All inputs
// : and outputs must be connected.
// <-----Cut code below this line---->
// IDELAYE2: Input Fixed or Variable Delay Element
// Kintex-7
// Xilinx HDL Language Template, version 2022.1
(* IODELAY_GROUP = <iodelay_group_name> *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
IDELAYE2 #(
.CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
.DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
.HIGH_PERFORMANCE_MODE("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
.IDELAY_TYPE("FIXED"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
.IDELAY_VALUE(0), // Input delay tap setting (0-31)
.PIPE_SEL("FALSE"), // Select pipelined mode, FALSE, TRUE
.REFCLK_FREQUENCY(200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
.SIGNAL_PATTERN("DATA") // DATA, CLOCK input signal
)
IDELAYE2_inst (
.CNTVALUEOUT(CNTVALUEOUT), // 5-bit output: Counter value output
.DATAOUT(DATAOUT), // 1-bit output: Delayed data output
.C(C), // 1-bit input: Clock input
.CE(CE), // 1-bit input: Active high enable increment/decrement input
.CINVCTRL(CINVCTRL), // 1-bit input: Dynamic clock inversion input
.CNTVALUEIN(CNTVALUEIN), // 5-bit input: Counter value input
.DATAIN(DATAIN), // 1-bit input: Internal delay data input
.IDATAIN(IDATAIN), // 1-bit input: Data input from the I/O
.INC(INC), // 1-bit input: Increment / Decrement tap delay input
.LD(LD), // 1-bit input: Load IDELAY_VALUE input
.LDPIPEEN(LDPIPEEN), // 1-bit input: Enable PIPELINE register to load data input
.REGRST(REGRST) // 1-bit input: Active-high reset tap-delay input
);
// End of IDELAYE2_inst instantiation
// IDELAYCTRL: IDELAYE2/ODELAYE2 Tap Delay Value Control
// Kintex-7
// Xilinx HDL Language Template, version 2022.1
(* IODELAY_GROUP = <iodelay_group_name> *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
IDELAYCTRL IDELAYCTRL_inst (
.RDY(RDY), // 1-bit output: Ready output
.REFCLK(REFCLK), // 1-bit input: Reference clock input
.RST(RST) // 1-bit input: Active high reset input
);
// End of IDELAYCTRL_inst instantiation
3.3 IDDR/ODDR
同样的,前面讲的RGMII的时序,输入/输出是双边沿数据信号(DDR),但对于FPGA内部而言,需要把双边沿转化为单边信号(SDR),而IDDR就是把输入双边信号转化为单边,ODDR则相反,把FPGA内部单沿信号转化为输出的双沿信号。如下方IDDR模版所示,DDR_CLK_EDGE
控制时序模式,一共三种"OPPOSITE_EDGE
", "SAME_EDGE
"以及"SAME_EDGE_PIPELINED
",它们决定了时钟C,输出Q1/Q2(单沿)以及输入D(双沿)的时序关系。本项目使用的IDDR时序模式是SAME_EDGE_PIPELINED
,即转换后的单沿数据是同时输入FPGA的。ODDR时序模式是SAME_EDGE
,即内部数据对齐发送,具体请参阅下文时序图。
// IDDR: Input Double Data Rate Input Register with Set, Reset
// and Clock Enable.
// Kintex-7
// Xilinx HDL Language Template, version 2022.1
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
);
// End of IDDR_inst instantiation
「OPPOSITE_EDGE」
「SAME_EDGE」
「SAME_EDGE_PIPELINED」
对于ODDR模版如下所示所示:
// ODDR: Output Double Data Rate Output Register with Set, Reset
// and Clock Enable.
// Kintex-7
// Xilinx HDL Language Template, version 2022.1
ODDR #(
.DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_inst (
.Q(Q), // 1-bit DDR output
.C(C), // 1-bit clock input
.CE(CE), // 1-bit clock enable input
.D1(D1), // 1-bit data input (positive edge)
.D2(D2), // 1-bit data input (negative edge)
.R(R), // 1-bit reset
.S(S) // 1-bit set
);
// End of ODDR_inst instantiation
「OPPOSITE_EDGE」
「SAME_EDGE」
差分SelectIO原语在设备pads之间有两个引脚,以显示差分对中的P和N通道引脚。N通道引脚有一个B后缀。IBUFDS和IBUFGDS原语相同,当差分输入缓冲区用作时钟输入时,使用IBUFGDS。(这是手册的原话,但这个项目原工程尽管接了时钟也是使用了IBUFDS)
// IBUFDS: Differential Input Buffer
// Kintex-7
// Xilinx HDL Language Template, version 2022.1
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IBUF_LOW_PWR("TRUE"), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_inst (
.O(O), // Buffer output
.I(I), // Diff_p buffer input (connect directly to top-level port)
.IB(IB) // Diff_n buffer input (connect directly to top-level port)
);
// End of IBUFDS_inst instantiation
4仿真工程
前面简单讲了顶层涉及的一些模块以及相关知识,接着我们讨论逻辑代码内部的模块分层以及对相关模块进行仿真。
我们先分模块仿真,首先传输数据本身,分为「axis接口的fifo」,「udp_complete」(包含udp和ip),「eth_axis_rx」,「eth_axis_tx」,以及「eth_mac_1g_rgmii_fifo」。
4.1AXIS仿真
由于axis同步fifo太简单,我仅仿真异步的axis的fifo,涉及的文件如下,主要实现了AXIS接口的异步FIFO,可以实现输入输出位宽调整,以及可以实现Frame模式。
axis_adapter.v : Parametrizable bus width adapter
axis_async_fifo.v : Parametrizable asynchronous FIFO
axis_async_fifo_adapter.v : FIFO/width adapter wrapper
testbech代码参数配置如下:
parameter DEPTH = 4096;// FIFO深度
parameter S_DATA_WIDTH = 32;// S接口位宽
parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8);// KEEP信号使能
parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8);
parameter M_DATA_WIDTH = 8;// M接口位宽
parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8);// KEEP接口使能
parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8);
parameter PIPELINE_OUTPUT = 2;// 输出信号寄存器数
parameter FRAME_FIFO = 0;// 是否开启Frame模式
parameter USER_BAD_FRAME_VALUE = 1'b1;
parameter USER_BAD_FRAME_MASK = 1'b1;
parameter DROP_BAD_FRAME = 0;
parameter DROP_WHEN_FULL = 0;
时钟配置:
always #20 s_clk = ~s_clk;// 25M
always #5 m_clk = ~m_clk;// 100M
简单的数据传输:
integer i;
parameter len = 64;// 传输数据长度为64
initial begin
wait (s_rst == 0);
#40;
for (i = 0; i < len; i=i+1 ) begin
wait(s_axis_tready == 1);
s_axis_tvalid <= 1;
s_axis_tdata <= s_axis_tdata +1;
#40;
end
s_axis_tlast <= 1;// 传输结束拉高s_axis_tlast
#40;
s_axis_tlast <= 0;
s_axis_tvalid <= 0;
end
使用modeslsim仿真:
正在上传…重新上传取消
25M时钟域输入,100M时钟域采样,输入32bit位宽,输出8bit位宽,仿真和预期一致。
FIFO的FRAME模式
我们把异步FIFO修改为FRAME模式,即一直到一帧数据传输完成(last拉高),FIFO才继续开始输出数据,配置如下:
parameter FRAME_FIFO = 1;// 是否开启Frame模式
parameter USER_BAD_FRAME_VALUE = 1'b1;
parameter USER_BAD_FRAME_MASK = 1'b1;
parameter DROP_BAD_FRAME = 1;
parameter DROP_WHEN_FULL = 1;
使用modeslsim仿真:
上图可见,直到last拉起前,FIFO才开始传输,仿真波形达到预期。
「总结」:本期主要介绍top层的一些原语知识点,逻辑结构先开个头仿真AXIS接口的异步FIFO,下期我们再分析项目的代码结构并进行仿真。
参考文献
-
ADI模拟对话53-03(2019年3月期刊)作者:Volker E. Goller
-
TCP/IP入门经典【美】Joe Casad 著(第5版)
-
TI,以太网PHY简介
-
TI,RGMII时序
-
赛灵思官网文档:PG292
-
赛灵思官网文档:PG210
-
赛灵思官网文档:PG211
-
米联FPGA 以太网 UDP 通信方案(PL)
-
赛灵思官网文档:UG471
最后
以上就是爱笑美女为你收集整理的以太网(UDP)开源Verilog专题(二)的全部内容,希望文章能够帮你解决以太网(UDP)开源Verilog专题(二)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复