diff --git a/hardware/gerber.zip b/PCB/gerber.zip similarity index 100% rename from hardware/gerber.zip rename to PCB/gerber.zip diff --git a/hardware/sch.pdf b/PCB/sch.pdf similarity index 100% rename from hardware/sch.pdf rename to PCB/sch.pdf diff --git a/README.md b/README.md index aa66d06..41a339e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -![test](https://img.shields.io/badge/test-passing-green.svg) -![docs](https://img.shields.io/badge/docs-passing-green.svg) -![platform](https://img.shields.io/badge/platform-Quartus|Vivado-blue.svg) +![语言](https://img.shields.io/badge/语言-systemverilog_(IEEE1800_2005)-CAD09D.svg) ![仿真](https://img.shields.io/badge/仿真-iverilog-green.svg) ![部署](https://img.shields.io/badge/部署-quartus-blue.svg) ![部署](https://img.shields.io/badge/部署-vivado-FF1010.svg) FPGA DDR-SDRAM =========================== @@ -19,7 +17,7 @@ FPGA DDR-SDRAM ------------| |-------------------------| |--------------| 用户逻辑 DDR1 控制器 DDR1 芯片 -很多低端 FPGA 开发板(例如 [DE0-Nano](https://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&No=593))使用 SDR-SDRAM 作为片外存储,而 DDR-SDRAM (DDR1) 比 SDR-SDRAM 容量更大,价格更低。且与SDR-SDRAM一样,DDR1也能使用低端FPGA的普通的IO管脚直接驱动。我编写了一个软核的 AXI4 接口的 DDR1 控制器(见 [RTL/ddr_sdram_ctrl.sv](./RTL/ddr_sdram_ctrl.sv) )。该控制器的特点有: +很多低端 FPGA 开发板使用 SDR-SDRAM 作为片外存储,而 DDR-SDRAM (DDR1) 比 SDR-SDRAM 容量更大,价格更低。且与SDR-SDRAM一样,DDR1也能使用低端FPGA的普通的IO管脚直接驱动。我编写了一个软核的 AXI4 接口的 DDR1 控制器。该控制器的特点有: * **平台无关** :纯 RTL 编写,可以在 Altera 和 Xilinx 等各种 FPGA 上运行。 * **兼容性强** :支持各种位宽和容量的 DDR1 (已在MICRON所有位宽和容量的DDR1型号上仿真通过)。 @@ -31,6 +29,8 @@ FPGA DDR-SDRAM 另外,由于各代 DDR-SDRAM(例如DDR3、DDR2、DDR1)的接口时序大同小异,本库也可以方便那些熟悉 Verilog 的人来学习 DDR-SDRAM 接口。 + + # 目录 * [简介](#简介) @@ -47,11 +47,13 @@ FPGA DDR-SDRAM * [示例程序](#示例程序) * [示例程序:自测](#示例程序:自测) * [示例程序:UART读写](#示例程序:UART读写) -* [Verilog仿真](#Verilog仿真) +* [仿真](#仿真) * [建立仿真工程](#建立仿真工程) * [运行仿真](#运行仿真) * [修改仿真参数](#修改仿真参数) + + # 硬件设计指南 对于 FPGA 的选型,只需要有足够数量的普通 IO 的 FPGA 就可以驱动 DDR1。DDR1 的 IO 电平标准往往是 SSTL-2 ,与 2.5V LVTTL 或 2.5V LVCMOS 兼容,因此相应的 FPGA 的 IO bank 的电源应该是 2.5V,且应在开发软件配置为 2.5V LVTTL 或 2.5V LVCMOS 。 @@ -74,17 +76,19 @@ FPGA DDR-SDRAM 为了进行展示,我用 Altera Cyclone IV 的最低廉的 FPGA (型号:EP4CE6E22C8N) 和 MICRON 的 64MB DDR1 (型号 MT46V64M8TG-6T)画了一个小板子,本库的所有例子可以直接在该板子上直接运行。(如果你要在自己的PCB设计中使用 DDR1 ,只要抄我这个板子就行) -| ![board-image](./images/board.jpg) | +| ![board-image](./figures/board.jpg) | | :---: | | 图:FPGA + DDR1 测试板 | -原理图见 [hardware/sch.pdf](./hardware/sch.pdf),PCB制造文件见 [hardware/gerber.zip](./hardware/gerber.zip) ,在布局布线时,使用双层板即可,不需像 DDR2 以上的设计那样刻意注意等长和阻抗匹配,因为该电路工作频率为双边沿 75MHz,不是特别高,只需注意让FPGA与DDR距离尽量近,布线尽量短即可。比如我在布局时,把DDR1芯片放在了FPGA芯片正对的背面,从而保证布线都较短。 +原理图见 PCB/sch.pdf ,PCB制造文件见 PCB/gerber.zip ,在布局布线时,使用双层板即可,不需像 DDR2 以上的设计那样刻意注意等长和阻抗匹配,因为该电路工作频率为双边沿 75MHz,不是特别高,只需注意让FPGA与DDR距离尽量近,布线尽量短即可。比如我在布局时,把DDR1芯片放在了FPGA芯片正对的背面,从而保证布线都较短。 -该板子的设计在立创EDA中开放,见[这里](https://oshwhub.com/wangxuan/fpga-ddr-ce-shi-ban)。 +该板子的设计在立创EDA中开放,见 [oshwhub.com/wangxuan/fpga-ddr-ce-shi-ban](https://oshwhub.com/wangxuan/fpga-ddr-ce-shi-ban)。 -# DDR1控制器 -DDR1 控制器代码见 [RTL/ddr_sdram_ctrl.sv](./RTL/ddr_sdram_ctrl.sv) ,是一个用 SystemVerilog 编写的模块,它能自动对 DDR1 进行初始化,并定时进行刷新(Refresh)。该模块有一个简化但完备的 AXI4 从接口,通过它可以完成对 DDR1 的读写。本节详细解释该模块的使用方法。 + +# DDR1控制器模块 + +DDR1 控制器代码见 RTL/ddr_sdram_ctrl.sv ,它能自动对 DDR1 进行初始化,并定时进行刷新(Refresh)。该模块有一个简化但完备的 AXI4 从接口,通过它可以完成对 DDR1 的读写。本节详细解释该模块的使用方法。 ## 模块参数 @@ -121,27 +125,25 @@ module ddr_sdram_ctrl #( 该模块需要一个时钟和一个复位进行驱动,如下: ```Verilog - input wire rstn, + input wire rstn_async, input wire clk, ``` -rstn 是低电平复位信号,正常工作时应该置高。clk 是驱动时钟,频率是用户时钟的 4 倍。 +rstn_async 是低电平复位信号,正常工作时应该置高。clk 是驱动时钟,频率是用户时钟的 4 倍。 -模块开始工作前,rstn信号应该置低,让模块复位,当 clk 的频率稳定后,可以把 rstn 置高,让模块处于工作状态。 - -如果 clk 一开始就是稳定的(例如直接来自晶振输入),那么 rstn 可以直接置为 1'b1 。 - -如果 clk 经过一段时间后才能稳定(例如来自 FPGA 的 PLL 或 MMCM),那么 rstn 应该接 PLL/MMCM 的 locked 信号,保证 PLL 锁相成功(稳定)后,模块再开始工作。 +模块开始工作前,rstn_async 信号应该置低,让模块复位,然后把 rstn_async 置高,解除复位。 ## 时钟频率的选取 -模块内使用寄存器分频 4 倍产生 DDR1 驱动时钟(ddr_ck_p/ddr_ck_n)和 AXI4 总线用户时钟(aclk)。本节讲述如何决定驱动时钟 clk 的频率。 +模块内将驱动时钟 clk 分频 4 倍产生 DDR1 时钟(ddr_ck_p/ddr_ck_n)和 AXI4 总线用户时钟(aclk)。本节讲述如何决定驱动时钟 clk 的频率。 -首先,时钟频率受限于 DDR1 芯片。考虑到所有的 DDR1 的接口频率至少为 75MHz ,则 clk 的下限是 75\*4=300MHz。而 clk 的上限就取决于 DDR1 的芯片型号,例如对于 MT46V64M8P-5B ,查芯片手册可知,-5B 后缀的 DDR1 在 CAS Latency (CL)=2 时最高时钟频率是 133MHz,则 clk 的上限是 133\*4=532MHz 。 +首先,时钟频率受限于 DDR1 芯片。考虑到所有的 DDR1 的接口频率至少为 75MHz ,则 clk 的下限是 75\*4=300MHz。 + +而 clk 的上限就也取决于 DDR1 的芯片型号,例如对于 MT46V64M8P-5B ,查芯片手册可知,-5B 后缀的 DDR1 在 CAS Latency (CL)=2 时最高时钟频率是 133MHz,则 clk 的上限是 133\*4=532MHz 。 > 注意:本控制器固定 CAS Latency (CL) = 2。 -另外,时钟频率的上限还受限于 FPGA 的速度,太高的时钟频率容易导致时序不收敛。该控制器充分考虑时序安全设计,大多数寄存器工作在频率较低用户时钟域;个别寄存器工作在用户时钟的 2 倍频率的时钟下,且输入端口的组合逻辑非常短;还有一个寄存器工作在高频的 clk 下,但输入端口直接来自于上一级的寄存器输出(没有组合逻辑)。因此,即使在速度级别很低的 EP4CE6E22C8N 上,在 300MHz 的驱动时钟下也能保证模块正确运行。在速度等级更高的 FPGA (例如 EP4CE22F17C6N)上,驱动时钟的频率可以更高(例如400MHz)。 +另外,时钟频率的上限还受限于 FPGA 的速度,太高的时钟频率容易导致时序不收敛。本设计充分考虑时序安全设计,大多数寄存器工作在频率较低用户时钟域;个别寄存器工作在用户时钟的 2 倍频率的时钟下,且输入端口的组合逻辑非常短;还有一个寄存器工作在高频的 clk 下,但输入端口直接来自于上一级的寄存器输出(没有组合逻辑)。因此,即使在速度级别很低的 EP4CE6E22C8N 上,在 300MHz 的驱动时钟下也能保证模块正确运行。在速度等级更高的 FPGA (例如 EP4CE22F17C6N)上,驱动时钟的频率可以更高(例如400MHz)。 ## 模块接口:DDR1接口 @@ -163,7 +165,7 @@ rstn 是低电平复位信号,正常工作时应该置高。clk 是驱动时 可以看出 DDR1 接口的一些信号的位宽是和参数有关的,用户需要根据 DDR1 的芯片选型来确定模块参数。详见 [位宽参数的确定](#位宽参数的确定)。 -想了解 DDR1 接口在初始化、读写、刷新时的波形,请进行 [Verilog仿真](#Verilog仿真)。 +想了解 DDR1 接口在初始化、读写、刷新时的波形,请进行 [仿真](#仿真)。 ## 模块接口:AXI4从接口 @@ -329,20 +331,24 @@ AXI4 总线的地址(awaddr和araddr)统一是字节地址,模块会根据 _______________________ ddr_a XXXXXX__RA_XXXXXXX_CA0_X_CA1_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + + # 示例程序 ## 示例程序:自测 -我基于我画的 [FPGA+DDR1测试板](#硬件设计示例) 做了一个 DDR1 读写自测程序,工程目录是 [example-selftest](./example-selftest),请用 Quartus II 13.1 打开它。 +我基于我画的 [FPGA+DDR1测试板](#硬件设计示例) 做了一个 DDR1 读写自测程序,工程目录是 example-selftest ,请用 Quartus 打开它。 该工程包含以下文件: | 文件名称 | 用途 | | :---- | :--- | -| [top.sv](./example-selftest/RTL/top.sv) | 顶层 | -| [axi_self_test_master.sv](./example-selftest/RTL/axi_self_test_master.sv) | 是 AXI4 主机,通过 AXI4 先把有规律的数据写入 DDR1,然后读回,比较读回的数据是否符合规律,并对不匹配的情况进行计数。 | -| [ddr_sdram_ctrl.sv](./RTL/ddr_sdram_ctrl.sv) | DDR1 控制器 | -| [pll.v](./example-selftest/IP/pll.v) | 使用板上 50MHz 晶振产生 300MHz 时钟,输出给 DDR1 控制器 | +| example-selftest/RTL/top.sv | 顶层 | +| example-selftest/RTL/axi_self_test_master.sv | 是 AXI4 主机,通过 AXI4 先把有规律的数据写入 DDR1,然后读回,比较读回的数据是否符合规律,并对不匹配的情况进行计数。 | +| example-selftest/RTL/pll.v | 使用板上 50MHz 晶振产生 300MHz 时钟,输出给 DDR1 控制器 | +| /RTL/ddr_sdram_ctrl.sv | DDR1 控制器 | + +该示例程序的行为是: **写入**:该工程开始运行后,会先通过 AXI4 把整个 DDR1 都写一遍,直接把地址字写入相应的地址。例如,如果 AXI4 的数据宽度是 16bit,那么地址 0x000002 处写 0x0001。地址 0x123456 处写 0x3456。 @@ -350,28 +356,26 @@ AXI4 总线的地址(awaddr和araddr)统一是字节地址,模块会根据 **SignalTap抓波形**:该工程包含一个 SignalTap 文件 stp1.stp,在程序运行时,可以用它查看 DDR1 接口上的波形。它以 error=1 为触发信号,因此如果读写自测没有出错,它就不会被触发。因为该工程随时都在读取 DDR1,要想看 DDR1 接口上的波形,直接按“停止”按钮即可。 -**修改 AXI4 突发长度**:在 [top.sv](./example-selftest/RTL/top.sv) 的第 82,83 行可以修改 WBURST_LEN 和 RBURST_LEN,从而修改自测时的写/读突发长度,该自测程序只支持 2^n-1 这种突发长度,即 WBURST_LEN 和 RBURST_LEN 必须取形如 0,1,3,7,15,31,…… 的值(注意,这只是我编写的自测程序的限制,DDR1 控制器是支持 0~255 之间的任意突发长度的。 +**修改 AXI4 突发长度**:在 top.sv 的第 82,83 行可以修改 WBURST_LEN 和 RBURST_LEN,从而修改自测时的写/读突发长度,该自测程序只支持 2^n-1 这种突发长度,即 WBURST_LEN 和 RBURST_LEN 必须取形如 0,1,3,7,15,31,…… 的值(注意,这只是我编写的自测程序的限制,DDR1 控制器是支持 0~255 之间的任意突发长度的。 > WBURST_LEN 和 RBURST_LEN 可以设置的不一样。 ## 示例程序:UART读写 -我基于我画的 [FPGA+DDR1测试板](#硬件设计示例) 做了一个 UART 读写程序,使用该程序,你可以通过 UART 命令,以不同的突发长度来读写 DDR1。工程目录是 [example-uart-read-write](./example-uart-read-write),请用 Quartus II 13.1 打开它。 +我基于我画的 [FPGA+DDR1测试板](#硬件设计示例) 做了一个 UART 读写程序,使用该程序,你可以通过 UART 命令,以不同的突发长度来读写 DDR1。工程目录是 example-uart-read-write ,请用 Quartus 打开它。 该工程包含以下文件: | 文件名称 | 用途 | | :---- | :--- | -| [top.sv](./example-uart-read-write/RTL/top.sv) | 顶层 | -| [uart2axi4.sv](./example-uart-read-write/RTL/uart2axi4.sv) | 是 AXI4 主机,能把 UART RX 收到的命令转换成 AXI4 读写操作,并把读操作读出的数据通过 UART TX 发送出去 | -| [uart_rx.sv](./example-uart-read-write/RTL/uart_rx.sv) | UART RX 接收器,被 uart2axi4.sv 调用 | -| [axis2uarttx.sv](./example-uart-read-write/RTL/axis2uarttx.sv) | UART TX 发送器,被 uart2axi4.sv 调用 | -| [ddr_sdram_ctrl.sv](./RTL/ddr_sdram_ctrl.sv) | DDR1 控制器 | -| [pll.v](./example-uart-read-write/IP/pll.v) | 使用板上 50MHz 晶振产生 300MHz 时钟,输出给 DDR1 控制器 | +| example-uart-read-write/RTL/top.sv | 顶层 | +| example-uart-read-write/RTL/uart2axi4.sv | 是 AXI4 主机,能把 UART RX 收到的命令转换成 AXI4 读写操作,并把读操作读出的数据通过 UART TX 发送出去 | +| example-uart-read-write/RTL/pll.v | 使用板上 50MHz 晶振产生 300MHz 时钟,输出给 DDR1 控制器 | +| RTL/ddr_sdram_ctrl.sv | DDR1 控制器 | -[FPGA+DDR1测试板](#硬件设计示例)上有一个 CH340E 芯片(USB 转 UART),因此插上 USB 线后就可以在电脑上看见 UART 对应的 COM 口(需要先在[这里](http://www.wch.cn/product/CH340.html)下载安装 CH341 的驱动)。 +[FPGA+DDR1测试板](#硬件设计示例)上有一个 CH340E 芯片(USB 转 UART),因此插上 USB 线后就可以在电脑上看见 UART 对应的 COM 口(需要先在 [www.wch.cn/product/CH340.html](http://www.wch.cn/product/CH340.html) 下载安装 CH341 的驱动)。 -工程上传 FPGA 后,双击打开我编写的一个串口小工具 [UartSession.exe](./UartSession.exe) ,根据提示打开板子对应的 COM 口,然后打如下的命令+回车,可以把 0x0123 0x4567 0x89ab 0xcdef 这 4 个数据写入起始地址0x12345。(AXI4总线上会产生一个突发长度为 4 的写操作)。 +工程上传 FPGA 后,双击打开我编写的一个串口小工具 UartSession.exe ,根据提示打开板子对应的 COM 口,然后打如下的命令+回车,可以把 0x0123 0x4567 0x89ab 0xcdef 这 4 个数据写入起始地址 0x12345。(AXI4总线上会产生一个突发长度为 4 的写操作)。 w12345 0123 4567 89ab cdef @@ -381,7 +385,7 @@ AXI4 总线的地址(awaddr和araddr)统一是字节地址,模块会根据 效果如下图,前4个数据 (0123 4567 89ab cdef) 就是我们已经写入 DDR1 的,后4个数据我们没写过,是 DDR1 初始化后自带的随机数据。 -| ![UART读写](./images/UartSession.png) | +| ![](./figures/UartSession.png) | | :--: | | 图:使用 UartSession.exe 测试 DDR1 读写 | @@ -393,44 +397,33 @@ AXI4 总线的地址(awaddr和araddr)统一是字节地址,模块会根据 r0 1e -# Verilog仿真 + + +# 仿真 ## 建立仿真工程 -仿真所需要的文件在目录 [sim-selftest](./sim-selftest) 里,你可以用以下 Verilog 文件建立仿真工程: +仿真所需要的文件在目录 SIM 里,其中: | 文件路径 | 用途 | | :---- | :--- | -| [sim-selftest/SRC/tb_ddr_ctrl.sv](./sim-selftest/SRC/tb_ddr_ctrl.sv) | 仿真顶层 | -| [sim-selftest/SRC/axi_self_test_master.sv](./sim-selftest/SRC/axi_self_test_master.sv) | 是 AXI4 主机,通过 AXI4 先把有规律的数据写入 DDR1,然后读回,比较读回的数据是否符合规律,并对不匹配的情况进行计数。 | -| [RTL/ddr_sdram_ctrl.sv](./RTL/ddr_sdram_ctrl.sv) | DDR1 控制器 | -| [sim-selftest/DDR_MODEL/ddr.v](./sim-selftest/DDR_MODEL/ddr.v) | [MICRON 公司提供的 DDR1 仿真模型](https://www.micron.com/products/dram/ddr-sdram/part-catalog/mt46v64m8p-5b) | -| [sim-selftest/DDR_MODEL/ddr_parameters.vh](./sim-selftest/DDR_MODEL/ddr_parameters.vh) | DDR1 仿真模型的参数配置文件 | +| tb_ddr_sdram_ctrl.sv | 仿真顶层 | +| axi_self_test_master.sv | 是 AXI4 主机,通过 AXI4 先把有规律的数据写入 DDR1,然后读回,比较读回的数据是否符合规律,并对不匹配的情况进行计数。 | +| micron_ddr_sdram_model.v | [MICRON 公司提供的 DDR1 仿真模型](https://www.micron.com/products/dram/ddr-sdram/part-catalog/mt46v64m8p-5b) | -该仿真工程的行为类似[自测程序](#示例程序:自测),[axi_self_test_master.sv](./sim-selftest/SRC/axi_self_test_master.sv) 作为 AXI4 主机,将有规律的数据写入 DDR1 中,只不过不是全部写入,而是只写入 DDR1 的前 16KB (因为仿真模型的存储空间有限),然后一轮一轮地反复读出数据,比较是否有不匹配的数据,若有,则在 error 信号上产生一个时钟周期的高电平。 +该仿真工程的行为和自测程序一样, axi_self_test_master.sv 作为 AXI4 主机,将有规律的数据写入 DDR1 中,只不过不是全部写入,而是只写入 DDR1 的前 16KB (因为仿真模型的存储空间有限),然后一轮一轮地反复读出数据,比较是否有不匹配的数据,若有,则在 error 信号上产生一个时钟周期的高电平。 ## 运行仿真 -建立工程后,直接运行仿真,得到如下波形。前 294us,AXI4 主机在进行前 16KB 的写入;294us 之后,AXI4 主机在不断的读出数据。error 信号一直为低电平说明读出的数据无误。 +使用 iverilog 进行仿真前,需要安装 iverilog ,见:[iverilog_usage](https://github.com/WangXuan95/WangXuan95/blob/main/iverilog_usage/iverilog_usage.md) -| ![仿真波形](./images/sim.png) | -| :--: | -| 图:仿真波形 | - -你可以展开查看 AXI4 总线和 DDR1 接口的波形细节。这里不做赘述。 +然后双击 tb_ddr_sdram_ctrl_run_iverilog.bat 运行仿真,然后可以打开生成的 dump.vcd 文件查看波形。 ## 修改仿真参数 -以上仿真默认配置的参数是使用 MT46V64M8 ,即 ROW_BITS=13,COL_BITS=11,DQ_BITS=8。如果想对其它型号的 DDR1 芯片进行仿真,需要修改的参数如下: +以上仿真默认配置的参数是使用 MT46V64M8 ,即 ROW_BITS=13,COL_BITS=11,DQ_BITS=8。 -* 仿真顶层中的 **BA_BITS**:见 [sim-selftest/SRC/tb_ddr_ctrl.sv](./sim-selftest/SRC/tb_ddr_ctrl.sv) 第 8 行。 -* DDR1 模型中的 **BA_BITS**:见 [sim-selftest/DDR_MODEL/ddr_parameters.vh](./sim-selftest/DDR_MODEL/ddr_parameters.vh) 第 35 行。 -* 仿真顶层中的 **ROW_BITS**:见 [sim-selftest/SRC/tb_ddr_ctrl.sv](./sim-selftest/SRC/tb_ddr_ctrl.sv) 第 9 行。 -* DDR1 模型中的 **ROW_BITS**:见 [sim-selftest/DDR_MODEL/ddr_parameters.vh](./sim-selftest/DDR_MODEL/ddr_parameters.vh) 第 36 行。 -* 仿真顶层中的 **COL_BITS**:见 [sim-selftest/SRC/tb_ddr_ctrl.sv](./sim-selftest/SRC/tb_ddr_ctrl.sv) 第 10 行。 -* DDR1 模型中的 **COL_BITS**:见 [sim-selftest/DDR_MODEL/ddr_parameters.vh](./sim-selftest/DDR_MODEL/ddr_parameters.vh) 第 37 行。 -* 仿真顶层中的 **DQ_LEVEL**:见 [sim-selftest/SRC/tb_ddr_ctrl.sv](./sim-selftest/SRC/tb_ddr_ctrl.sv) 第 11 行。 -* DDR1 模型中的 **DQ_BITS**:见 [sim-selftest/DDR_MODEL/ddr_parameters.vh](./sim-selftest/DDR_MODEL/ddr_parameters.vh) 第 38 行。 +如果想对其它型号的 DDR1 芯片进行仿真,你需要在 tb_ddr_sdram_ctrl.sv 和 micron_ddr_sdram_model.v 里修改它们(注意两个文件要同步修改!) 对于 MICRON 公司的 DDR1 系列,这些参数应该这样修改: @@ -446,7 +439,9 @@ AXI4 总线的地址(awaddr和araddr)统一是字节地址,模块会根据 | MT46V32M16 | 2 | 13 | 10 | 2 | 16 | | MT46V64M16 | 2 | 14 | 10 | 2 | 16 | -另外,你可以修改 [tb_ddr_ctrl.sv](./sim-selftest/SRC/tb_ddr_ctrl.sv) 的第 18 和 19 行来修改仿真时的突发读写的长度。 +另外,你可以修改 tb_ddr_sdram_ctrl.sv 的第 18 和 19 行来修改仿真时的突发读写的长度。 + + # 参考资料 diff --git a/RTL/ddr_sdram_ctrl.sv b/RTL/ddr_sdram_ctrl.sv index 48a7eef..865bb03 100644 --- a/RTL/ddr_sdram_ctrl.sv +++ b/RTL/ddr_sdram_ctrl.sv @@ -1,4 +1,11 @@ -`timescale 1 ns/1 ns + +//-------------------------------------------------------------------------------------------------------- +// Module : ddr_sdram_ctrl +// Type : synthesizable, IP's top +// Standard: SystemVerilog 2005 (IEEE1800-2005) +// Function: DDR-SDRAM (DDR1) controller +// with AXI4 interface +//-------------------------------------------------------------------------------------------------------- module ddr_sdram_ctrl #( parameter READ_BUFFER = 1, @@ -14,9 +21,9 @@ module ddr_sdram_ctrl #( parameter [7:0] tR2I = 8'd7 ) ( // driving clock and reset - input wire rstn, + input wire rstn_async, input wire clk, // driving clock, typically 300~532MHz - // user interface ( meta AXI4 ) + // user interface ( AXI4 ) output reg aresetn, output reg aclk, // freq = F(clk)/4 input wire awvalid, @@ -53,17 +60,16 @@ module ddr_sdram_ctrl #( localparam DQS_BITS = ((1<0) - {ddr_ba, ddr_a, col_addr, trash_lsb_addr} <= awaddr; - else - {ddr_ba, ddr_a, col_addr} <= awaddr; + {ddr_ba, ddr_a, col_addr} <= awaddr[BA_BITS+ROW_BITS+COL_BITS+DQ_LEVEL-2:DQ_LEVEL]; burst_len <= awlen; stat <= WPRE; end else if(arvalid & read_accessible) begin ddr_ras_n <= 1'b0; - if(DQ_LEVEL>0) - {ddr_ba, ddr_a, col_addr, trash_lsb_addr} <= araddr; - else - {ddr_ba, ddr_a, col_addr} <= araddr; + {ddr_ba, ddr_a, col_addr} <= araddr[BA_BITS+ROW_BITS+COL_BITS+DQ_LEVEL-2:DQ_LEVEL]; burst_len <= arlen; stat <= RPRE; end @@ -388,7 +410,7 @@ always @ (posedge aclk or negedge aresetn) // ------------------------------------------------------------------------------------- // dq and dqs generate for output (write) // ------------------------------------------------------------------------------------- -always @ (posedge clk2) begin +always @ (posedge clk2) if(~aclk) begin o_dqs_c <= 1'b0; o_d_c <= o_v_a ? o_dl_a : '0; @@ -396,7 +418,6 @@ always @ (posedge clk2) begin o_dqs_c <= o_v_b; o_d_c <= o_v_b ? o_dh_b : '0; end -end // ------------------------------------------------------------------------------------- // dq delay for output (write) @@ -446,20 +467,60 @@ always @ (posedge aclk or negedge aresetn) // data buffer for read // ------------------------------------------------------------------------------------- generate if(READ_BUFFER) begin - SyncFIFO #( - .AWIDTH ( 10 ), - .DWIDTH ( 1 + (8<1) - txshift <= hex2ascii(data[(hexcnt-2)*4+:4]); - else if(endofline) - txshift <= "\n"; - else - txshift <= " "; - txcnt <= 11; - end - end - end else if(emptyn) begin - uart_txb <= 1'b1; - hexcnt <= 2 + TX_WIDTH; - txcnt <= 0; - fifo_rpt <= fifo_rpt_next; - end - end - -ram_for_axi_stream_to_uart_tx_fifo #( - .ADDR_LEN ( FIFO_ASIZE ), - .DATA_LEN ( DATA_WIDTH + 1 ) -) ram_for_uart_tx_fifo_inst ( - .clk ( aclk ), - .wr_req ( tvalid & tready ), - .wr_addr ( fifo_wpt ), - .wr_data ( {tlast, tdata} ), - .rd_addr ( fifo_rpt ), - .rd_data ( {fifo_tlast,fifo_data} ) -); - -endmodule - - - - - - -module ram_for_axi_stream_to_uart_tx_fifo #( - parameter ADDR_LEN = 12, - parameter DATA_LEN = 8 -) ( - input logic clk, - input logic wr_req, - input logic [ADDR_LEN-1:0] rd_addr, wr_addr, - output logic [DATA_LEN-1:0] rd_data, - input logic [DATA_LEN-1:0] wr_data -); - -localparam RAM_SIZE = (1<=CLK_DIV) : (cnt>=CLK_DIV-1) ) begin + if(status==0) begin + if(shift == 6'b111_000) + status <= 1; + end else begin + if(status[5] == 1'b0) begin + if(status[1:0] == 2'b11) + databuf <= {recvbit, databuf[7:1]}; + status <= status + 5'b1; + end else begin + if(status<62) begin + status <= 62; + data <= databuf; + done <= 1'b1; + end else begin + status <= status + 6'd1; + end + end + end + shift <= {shift[4:0], rxr}; + supercnt <= supercnt + 3'h1; + cnt <= 0; + end else + cnt <= cnt + 1; + end + +endmodule + + + + + + + + + + +module axis2uarttx #( + parameter CLK_DIV = 434, + parameter DATA_WIDTH = 32, + parameter FIFO_ASIZE = 8 +) ( + // AXI-stream (slave) side + input logic aclk, aresetn, + input logic tvalid, tlast, + output logic tready, + input logic [DATA_WIDTH-1:0] tdata, + // UART TX signal + output logic uart_tx +); +localparam TX_WIDTH = (DATA_WIDTH+3) / 4; + +function automatic logic [7:0] hex2ascii (input [3:0] hex); + return {4'h3, hex} + ((hex<4'hA) ? 8'h0 : 8'h7) ; +endfunction + +logic uart_txb; +logic [FIFO_ASIZE-1:0] fifo_rpt='0, fifo_wpt='0; +wire [FIFO_ASIZE-1:0] fifo_wpt_next = fifo_wpt + {{(FIFO_ASIZE-1){1'b0}}, 1'b1}; +wire [FIFO_ASIZE-1:0] fifo_rpt_next = fifo_rpt + {{(FIFO_ASIZE-1){1'b0}}, 1'b1}; +logic [31:0] cyccnt=0, hexcnt=0, txcnt=0; +logic [ 7:0] txshift = '1; +logic fifo_tlast; +logic [DATA_WIDTH-1:0] fifo_data; +logic endofline = 1'b0; +logic [TX_WIDTH*4-1:0] data='0; +wire emptyn = (fifo_rpt != fifo_wpt); +assign tready = (fifo_rpt != fifo_wpt_next) & aresetn; + +always @ (posedge aclk or negedge aresetn) + if(~aresetn) + uart_tx <= 1'b1; + else begin + uart_tx <= uart_txb; + end + +always @ (posedge aclk or negedge aresetn) + if(~aresetn) + fifo_wpt <= '0; + else begin + if(tvalid & tready) fifo_wpt <= fifo_wpt_next; + end + +always @ (posedge aclk or negedge aresetn) + if(~aresetn) + cyccnt <= 0; + else + cyccnt <= (cyccnt1) + txshift <= hex2ascii(data[(hexcnt-2)*4+:4]); + else if(endofline) + txshift <= 8'h0A; + else + txshift <= 8'h20; + txcnt <= 11; + end + end + end else if(emptyn) begin + uart_txb <= 1'b1; + hexcnt <= 2 + TX_WIDTH; + txcnt <= 0; + fifo_rpt <= fifo_rpt_next; + end + end + +ram_for_axi_stream_to_uart_tx_fifo #( + .ADDR_LEN ( FIFO_ASIZE ), + .DATA_LEN ( DATA_WIDTH + 1 ) +) ram_for_uart_tx_fifo_inst ( + .clk ( aclk ), + .wr_req ( tvalid & tready ), + .wr_addr ( fifo_wpt ), + .wr_data ( {tlast, tdata} ), + .rd_addr ( fifo_rpt ), + .rd_data ( {fifo_tlast,fifo_data} ) +); + +endmodule + + + + + + +module ram_for_axi_stream_to_uart_tx_fifo #( + parameter ADDR_LEN = 12, + parameter DATA_LEN = 8 +) ( + input logic clk, + input logic wr_req, + input logic [ADDR_LEN-1:0] rd_addr, wr_addr, + output logic [DATA_LEN-1:0] rd_data, + input logic [DATA_LEN-1:0] wr_data +); + +localparam RAM_SIZE = (1<=CLK_DIV) : (cnt>=CLK_DIV-1) ) begin - if(status==0) begin - if(shift == 6'b111_000) - status <= 1; - end else begin - if(status[5] == 1'b0) begin - if(status[1:0] == 2'b11) - databuf <= {recvbit, databuf[7:1]}; - status <= status + 5'b1; - end else begin - if(status<62) begin - status <= 62; - data <= databuf; - done <= 1'b1; - end else begin - status <= status + 6'd1; - end - end - end - shift <= {shift[4:0], rxr}; - supercnt <= supercnt + 3'h1; - cnt <= 0; - end else - cnt <= cnt + 1; - end - -endmodule \ No newline at end of file diff --git a/example-uart-read-write/ddr_test.qsf b/example-uart-read-write/ddr_test.qsf index bb6648c..acfad47 100644 --- a/example-uart-read-write/ddr_test.qsf +++ b/example-uart-read-write/ddr_test.qsf @@ -41,19 +41,18 @@ set_global_assignment -name DEVICE EP4CE6E22C8 set_global_assignment -name TOP_LEVEL_ENTITY top set_global_assignment -name ORIGINAL_QUARTUS_VERSION 13.1 set_global_assignment -name PROJECT_CREATION_TIME_DATE "17:45:24 JANUARY 23, 2021" -set_global_assignment -name LAST_QUARTUS_VERSION 13.1 +set_global_assignment -name LAST_QUARTUS_VERSION "18.1.0 Standard Edition" set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1 set_global_assignment -name NOMINAL_CORE_SUPPLY_VOLTAGE 1.2V set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "2.5 V" + set_global_assignment -name SYSTEMVERILOG_FILE RTL/top.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/ddr_sdram_ctrl.sv set_global_assignment -name SYSTEMVERILOG_FILE RTL/uart2axi4.sv -set_global_assignment -name SYSTEMVERILOG_FILE RTL/axis2uarttx.sv -set_global_assignment -name SYSTEMVERILOG_FILE RTL/uart_rx.sv -set_global_assignment -name VERILOG_FILE IP/pll.v +set_global_assignment -name VERILOG_FILE RTL/pll.v +set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/ddr_sdram_ctrl.sv set_location_assignment PIN_23 -to clk50m diff --git a/images/UartSession.png b/figures/UartSession.png similarity index 100% rename from images/UartSession.png rename to figures/UartSession.png diff --git a/images/board.jpg b/figures/board.jpg similarity index 100% rename from images/board.jpg rename to figures/board.jpg diff --git a/images/sim.png b/images/sim.png deleted file mode 100644 index b0df3e5..0000000 Binary files a/images/sim.png and /dev/null differ diff --git a/sim-selftest/DDR_MODEL/ddr_parameters.vh b/sim-selftest/DDR_MODEL/ddr_parameters.vh deleted file mode 100644 index 6c37045..0000000 --- a/sim-selftest/DDR_MODEL/ddr_parameters.vh +++ /dev/null @@ -1,124 +0,0 @@ -/**************************************************************************************** -* -* Disclaimer This software code and all associated documentation, comments or other -* of Warranty: information (collectively "Software") is provided "AS IS" without -* warranty of any kind. MICRON TECHNOLOGY, INC. ("MTI") EXPRESSLY -* DISCLAIMS ALL WARRANTIES EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -* TO, NONINFRINGEMENT OF THIRD PARTY RIGHTS, AND ANY IMPLIED WARRANTIES -* OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE. MTI DOES NOT -* WARRANT THAT THE SOFTWARE WILL MEET YOUR REQUIREMENTS, OR THAT THE -* OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE. -* FURTHERMORE, MTI DOES NOT MAKE ANY REPRESENTATIONS REGARDING THE USE OR -* THE RESULTS OF THE USE OF THE SOFTWARE IN TERMS OF ITS CORRECTNESS, -* ACCURACY, RELIABILITY, OR OTHERWISE. THE ENTIRE RISK ARISING OUT OF USE -* OR PERFORMANCE OF THE SOFTWARE REMAINS WITH YOU. IN NO EVENT SHALL MTI, -* ITS AFFILIATED COMPANIES OR THEIR SUPPLIERS BE LIABLE FOR ANY DIRECT, -* INDIRECT, CONSEQUENTIAL, INCIDENTAL, OR SPECIAL DAMAGES (INCLUDING, -* WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, BUSINESS INTERRUPTION, -* OR LOSS OF INFORMATION) ARISING OUT OF YOUR USE OF OR INABILITY TO USE -* THE SOFTWARE, EVEN IF MTI HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -* DAMAGES. Because some jurisdictions prohibit the exclusion or -* limitation of liability for consequential or incidental damages, the -* above limitation may not apply to you. -* -* Copyright 2003 Micron Technology, Inc. All rights reserved. -* -****************************************************************************************/ - -`define sg5B - - - parameter no_halt = 1; // If set to 1, the model won't halt on command sequence/major errors - parameter DEBUG = 1; // Turn on DEBUG message - - - parameter BA_BITS = 2; // Set this parmaeter to control how many Bank Address bits are used - parameter ROW_BITS = 13; // Set this parameter to control how many Address bits are used - parameter COL_BITS = 11; // Set this parameter to control how many Column bits are used - parameter DQ_BITS = 8; // Set this parameter to control how many Data bits are used - - - parameter ADDR_BITS = ROW_BITS; - - parameter part_mem_bits = 15; // Set this parameter to control how many unique addresses are used - - parameter full_mem_bits = BA_BITS+ROW_BITS+COL_BITS; // Set this parameter to control how many unique addresses are used - - parameter DQS_BITS = (DQ_BITS + 4) / 8; - parameter DM_BITS = DQS_BITS; - - - -`ifdef sg5B // Timing Parameters for -5B (CL = 3) - parameter tCK = 5.0; // tCK ns Nominal Clock Cycle Time - parameter tDQSQ = 0.4; // tDQSQ ns DQS-DQ skew, DQS to last DQ valid, per group, per access - parameter tMRD = 10.0; // tMRD ns Load Mode Register command cycle time - parameter tRAP = 15.0; // tRAP ns ACTIVE to READ with Auto precharge command - parameter tRAS = 40.0; // tRAS ns Active to Precharge command time - parameter tRC = 55.0; // tRC ns Active to Active/Auto Refresh command time - parameter tRFC = 70.0; // tRFC ns Refresh to Refresh Command interval time - parameter tRCD = 15.0; // tRCD ns Active to Read/Write command time - parameter tRP = 15.0; // tRP ns Precharge command period - parameter tRRD = 10.0; // tRRD ns Active bank a to Active bank b command time - parameter tWR = 15.0; // tWR ns Write recovery time -`else `ifdef sg6T // Timing Parameters for -6T (CL = 2.5) - parameter tCK = 6.0; // tCK ns Nominal Clock Cycle Time - parameter tDQSQ = 0.45; // tDQSQ ns DQS-DQ skew, DQS to last DQ valid, per group, per access - parameter tMRD = 12.0; // tMRD ns Load Mode Register command cycle time - parameter tRAP = 15.0; // tRAP ns ACTIVE to READ with Auto precharge command - parameter tRAS = 42.0; // tRAS ns Active to Precharge command time - parameter tRC = 60.0; // tRC ns Active to Active/Auto Refresh command time - parameter tRFC = 72.0; // tRFC ns Refresh to Refresh Command interval time - parameter tRCD = 15.0; // tRCD ns Active to Read/Write command time - parameter tRP = 15.0; // tRP ns Precharge command period - parameter tRRD = 12.0; // tRRD ns Active bank a to Active bank b command time - parameter tWR = 15.0; // tWR ns Write recovery time -`else `ifdef sg6 // Timing Parameters for -6 (CL = 2.5) - parameter tCK = 6.0; // tCK ns Nominal Clock Cycle Time - parameter tDQSQ = 0.4; // tDQSQ ns DQS-DQ skew, DQS to last DQ valid, per group, per access - parameter tMRD = 12.0; // tMRD ns Load Mode Register command cycle time - parameter tRAP = 15.0; // tRAP ns ACTIVE to READ with Auto precharge command - parameter tRAS = 42.0; // tRAS ns Active to Precharge command time - parameter tRC = 60.0; // tRC ns Active to Active/Auto Refresh command time - parameter tRFC = 72.0; // tRFC ns Refresh to Refresh Command interval time - parameter tRCD = 15.0; // tRCD ns Active to Read/Write command time - parameter tRP = 15.0; // tRP ns Precharge command period - parameter tRRD = 12.0; // tRRD ns Active bank a to Active bank b command time - parameter tWR = 15.0; // tWR ns Write recovery time -`else `ifdef sg75E // Timing Parameters for -75E (CL = 2) - parameter tCK = 7.5; // tCK ns Nominal Clock Cycle Time - parameter tDQSQ = 0.5; // tDQSQ ns DQS-DQ skew, DQS to last DQ valid, per group, per access - parameter tMRD = 15.0; // tMRD ns Load Mode Register command cycle time - parameter tRAP = 15.0; // tRAP ns ACTIVE to READ with Auto precharge command - parameter tRAS = 40.0; // tRAS ns Active to Precharge command time - parameter tRC = 60.0; // tRC ns Active to Active/Auto Refresh command time - parameter tRFC = 75.0; // tRFC ns Refresh to Refresh Command interval time - parameter tRCD = 15.0; // tRCD ns Active to Read/Write command time - parameter tRP = 15.0; // tRP ns Precharge command period - parameter tRRD = 15.0; // tRRD ns Active bank a to Active bank b command time - parameter tWR = 15.0; // tWR ns Write recovery time -`else `ifdef sg75Z // Timing Parameters for -75Z (CL = 2) - parameter tCK = 7.5; // tCK ns Nominal Clock Cycle Time - parameter tDQSQ = 0.5; // tDQSQ ns DQS-DQ skew, DQS to last DQ valid, per group, per access - parameter tMRD = 15.0; // tMRD ns Load Mode Register command cycle time - parameter tRAP = 20.0; // tRAP ns ACTIVE to READ with Auto precharge command - parameter tRAS = 40.0; // tRAS ns Active to Precharge command time - parameter tRC = 65.0; // tRC ns Active to Active/Auto Refresh command time - parameter tRFC = 75.0; // tRFC ns Refresh to Refresh Command interval time - parameter tRCD = 20.0; // tRCD ns Active to Read/Write command time - parameter tRP = 20.0; // tRP ns Precharge command period - parameter tRRD = 15.0; // tRRD ns Active bank a to Active bank b command time - parameter tWR = 15.0; // tWR ns Write recovery time -`else `define sg75 // Timing Parameters for -75 (CL = 2.5) - parameter tCK = 7.5; // tCK ns Nominal Clock Cycle Time - parameter tDQSQ = 0.5; // tDQSQ ns DQS-DQ skew, DQS to last DQ valid, per group, per access - parameter tMRD = 15.0; // tMRD ns Load Mode Register command cycle time - parameter tRAP = 20.0; // tRAP ns ACTIVE to READ with Auto precharge command - parameter tRAS = 40.0; // tRAS ns Active to Precharge command time - parameter tRC = 65.0; // tRC ns Active to Active/Auto Refresh command time - parameter tRFC = 75.0; // tRFC ns Refresh to Refresh Command interval time - parameter tRCD = 20.0; // tRCD ns Active to Read/Write command time - parameter tRP = 20.0; // tRP ns Precharge command period - parameter tRRD = 15.0; // tRRD ns Active bank a to Active bank b command time - parameter tWR = 15.0; // tWR ns Write recovery time -`endif `endif `endif `endif `endif