This commit is contained in:
WangXuan95 2022-03-30 02:03:32 +08:00
parent 4d296af72f
commit 3a71549cb8
22 changed files with 664 additions and 641 deletions

117
README.md
View File

@ -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) 的第 8283 行可以修改 WBURST_LEN 和 RBURST_LEN从而修改自测时的写/读突发长度,该自测程序只支持 2^n-1 这种突发长度,即 WBURST_LEN 和 RBURST_LEN 必须取形如 0,1,3,7,15,31,…… 的值注意这只是我编写的自测程序的限制DDR1 控制器是支持 0~255 之间的任意突发长度的。
**修改 AXI4 突发长度**:在 top.sv 的第 8283 行可以修改 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 信号上产生一个时钟周期的高电平。
## 运行仿真
建立工程后,直接运行仿真,得到如下波形。前 294usAXI4 主机在进行前 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=13COL_BITS=11DQ_BITS=8。如果想对其它型号的 DDR1 芯片进行仿真,需要修改的参数如下:
以上仿真默认配置的参数是使用 MT46V64M8 ,即 ROW_BITS=13COL_BITS=11DQ_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 行来修改仿真时的突发读写的长度。
# 参考资料

View File

@ -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<<DQ_LEVEL)+1)/2;
reg clk2;
reg init_done;
reg [2:0] ref_idle, ref_real;
reg [9:0] ref_cnt;
reg [7:0] cnt;
enum logic [3:0] {RESET, IDLE, CLEARDLL, REFRESH, WPRE, WRITE, WRESP, WWAIT, RPRE, READ, RRESP, RWAIT} stat;
reg clk2 = '0;
reg init_done = '0;
reg [2:0] ref_idle = 3'd1, ref_real = '0;
reg [9:0] ref_cnt = '0;
reg [7:0] cnt = '0;
enum logic [3:0] {RESET, IDLE, CLEARDLL, REFRESH, WPRE, WRITE, WRESP, WWAIT, RPRE, READ, RRESP, RWAIT} stat = RESET;
reg [7:0] burst_len;
reg [7:0] burst_len = '0;
wire burst_last = cnt==burst_len;
reg [DQ_LEVEL-1:0] trash_lsb_addr;
reg [COL_BITS-2:0] col_addr;
reg [COL_BITS-2:0] col_addr = '0;
wire [ROW_BITS-1:0] ddr_a_col;
generate if(COL_BITS>10) begin
@ -73,55 +79,77 @@ end else begin
end endgenerate
wire read_accessible, read_respdone;
reg output_enable, output_enable_d1, output_enable_d2;
reg output_enable='0, output_enable_d1='0, output_enable_d2='0;
reg o_v_a;
reg [(4<<DQ_LEVEL)-1:0] o_dh_a;
reg [(4<<DQ_LEVEL)-1:0] o_dl_a;
reg o_v_b;
reg [(4<<DQ_LEVEL)-1:0] o_dh_b;
reg o_dqs_c;
reg [(4<<DQ_LEVEL)-1:0] o_d_c;
reg [(4<<DQ_LEVEL)-1:0] o_d_d;
reg o_v_a = '0;
reg [(4<<DQ_LEVEL)-1:0] o_dh_a = '0;
reg [(4<<DQ_LEVEL)-1:0] o_dl_a = '0;
reg o_v_b = '0;
reg [(4<<DQ_LEVEL)-1:0] o_dh_b = '0;
reg o_dqs_c = '0;
reg [(4<<DQ_LEVEL)-1:0] o_d_c = '0;
reg [(4<<DQ_LEVEL)-1:0] o_d_d = '0;
reg i_v_a;
reg i_l_a;
reg i_v_b;
reg i_l_b;
reg i_v_c;
reg i_l_c;
reg i_dqs_c;
reg [(4<<DQ_LEVEL)-1:0] i_d_c;
reg i_v_d;
reg i_l_d;
reg [(8<<DQ_LEVEL)-1:0] i_d_d;
reg i_v_e;
reg i_l_e;
reg [(8<<DQ_LEVEL)-1:0] i_d_e;
reg i_v_a = '0;
reg i_l_a = '0;
reg i_v_b = '0;
reg i_l_b = '0;
reg i_v_c = '0;
reg i_l_c = '0;
reg i_dqs_c = '0;
reg [(4<<DQ_LEVEL)-1:0] i_d_c = '0;
reg i_v_d = '0;
reg i_l_d = '0;
reg [(8<<DQ_LEVEL)-1:0] i_d_d = '0;
reg i_v_e = '0;
reg i_l_e = '0;
reg [(8<<DQ_LEVEL)-1:0] i_d_e = '0;
// -------------------------------------------------------------------------------------
// constants defination and assignment
// -------------------------------------------------------------------------------------
reg [ROW_BITS-1:0] DDR_A_DEFAULT, DDR_A_MR0, DDR_A_MR_CLEAR_DLL;
always_comb begin
DDR_A_DEFAULT = '0;
DDR_A_MR0 = '0;
DDR_A_MR_CLEAR_DLL = '0;
DDR_A_DEFAULT[10] = 1'b1;
DDR_A_MR0[0] = 1'b1;
DDR_A_MR0[3] = 1'b1;
DDR_A_MR0[5] = 1'b1;
DDR_A_MR0[8] = 1'b1;
DDR_A_MR_CLEAR_DLL[0] = 1'b1;
DDR_A_MR_CLEAR_DLL[3] = 1'b1;
DDR_A_MR_CLEAR_DLL[5] = 1'b1;
end
localparam [ROW_BITS-1:0] DDR_A_DEFAULT = (ROW_BITS)'('b0100_0000_0000);
localparam [ROW_BITS-1:0] DDR_A_MR0 = (ROW_BITS)'('b0001_0010_1001);
localparam [ROW_BITS-1:0] DDR_A_MR_CLEAR_DLL = (ROW_BITS)'('b0000_0010_1001);
initial ddr_cs_n = 1'b1;
initial ddr_ras_n = 1'b1;
initial ddr_cas_n = 1'b1;
initial ddr_we_n = 1'b1;
initial ddr_ba = '0;
initial ddr_a = DDR_A_DEFAULT;
initial {aresetn, aclk} = '0;
// -------------------------------------------------------------------------------------
// generate reset sync with clk
// -------------------------------------------------------------------------------------
reg rstn_clk = '0;
reg [1:0] rstn_clk_l = '0;
always @ (posedge clk or negedge rstn_async)
if(~rstn_async)
{rstn_clk, rstn_clk_l} <= '0;
else
{rstn_clk, rstn_clk_l} <= {rstn_clk_l, 1'b1};
// -------------------------------------------------------------------------------------
// generate reset sync with aclk
// -------------------------------------------------------------------------------------
reg rstn_aclk = '0;
reg [1:0] rstn_aclk_l = '0;
always @ (posedge aclk or negedge rstn_async)
if(~rstn_async)
{rstn_aclk, rstn_aclk_l} <= '0;
else
{rstn_aclk, rstn_aclk_l} <= {rstn_aclk_l, 1'b1};
// -------------------------------------------------------------------------------------
// generate clocks
// -------------------------------------------------------------------------------------
always @ (posedge clk or negedge rstn)
if(~rstn)
always @ (posedge clk or negedge rstn_clk)
if(~rstn_clk)
{aclk,clk2} <= 2'b00;
else
{aclk,clk2} <= {aclk,clk2} + 2'b01;
@ -129,8 +157,8 @@ always @ (posedge clk or negedge rstn)
// -------------------------------------------------------------------------------------
// generate user reset
// -------------------------------------------------------------------------------------
always @ (posedge aclk or negedge rstn)
if(~rstn)
always @ (posedge aclk or negedge rstn_aclk)
if(~rstn_aclk)
aresetn <= 1'b0;
else
aresetn <= init_done;
@ -138,8 +166,8 @@ always @ (posedge aclk or negedge rstn)
// -------------------------------------------------------------------------------------
// refresh wptr self increasement
// -------------------------------------------------------------------------------------
always @ (posedge aclk or negedge rstn)
if(~rstn) begin
always @ (posedge aclk or negedge rstn_aclk)
if(~rstn_aclk) begin
ref_cnt <= '0;
ref_idle <= 3'd1;
end else begin
@ -178,8 +206,8 @@ assign arready = stat==IDLE && init_done && ref_real==ref_idle && ~awvalid && re
// -------------------------------------------------------------------------------------
// main FSM for generating DDR-SDRAM behavior
// -------------------------------------------------------------------------------------
always @ (posedge aclk or negedge rstn)
if(~rstn) begin
always @ (posedge aclk or negedge rstn_aclk)
if(~rstn_aclk) begin
ddr_cs_n <= 1'b1;
ddr_ras_n <= 1'b1;
ddr_cas_n <= 1'b1;
@ -231,18 +259,12 @@ always @ (posedge aclk or negedge rstn)
stat <= CLEARDLL;
end else if(awvalid) begin
ddr_ras_n <= 1'b0;
if(DQ_LEVEL>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<<DQ_LEVEL) )
) read_buffer_i (
.rstn ( aresetn ),
.clk ( aclk ),
.emptyn ( ),
.itvalid ( i_v_e ),
.itready ( ),
.itdata ( {i_l_e, i_d_e} ),
.otvalid ( rvalid ),
.otready ( rready ),
.otdata ( {rlast, rdata} )
);
localparam AWIDTH = 10;
localparam DWIDTH = 1 + (8<<DQ_LEVEL);
reg [AWIDTH-1:0] wpt = '0, rpt = '0;
reg dvalid = '0, valid = '0;
reg [DWIDTH-1:0] datareg = '0;
wire rreq;
reg [DWIDTH-1:0] fifo_rdata;
wire emptyn = rpt != wpt;
wire itready = rpt != (wpt + (AWIDTH)'(1));
assign rvalid = valid | dvalid;
assign rreq = emptyn & ( rready | ~rvalid );
assign {rlast, rdata} = dvalid ? fifo_rdata : datareg;
always @ (posedge aclk or negedge aresetn)
if(~aresetn)
wpt <= 0;
else if(i_v_e & itready)
wpt <= wpt + (AWIDTH)'(1);
always @ (posedge aclk or negedge aresetn)
if(~aresetn)
rpt <= 0;
else if(rreq & emptyn)
rpt <= rpt + (AWIDTH)'(1);
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
dvalid <= 1'b0;
valid <= 1'b0;
datareg <= 0;
end else begin
dvalid <= rreq;
if(dvalid)
datareg <= fifo_rdata;
if(rready)
valid <= 1'b0;
else if(dvalid)
valid <= 1'b1;
end
reg [DWIDTH-1:0] mem [(1<<AWIDTH)];
always @ (posedge aclk)
if(i_v_e)
mem[wpt] <= {i_l_e, i_d_e};
always @ (posedge aclk)
fifo_rdata <= mem[rpt];
assign read_accessible = ~rvalid;
assign read_respdone = rvalid;
end else begin
@ -471,123 +532,3 @@ end else begin
end endgenerate
endmodule
// --------------------------------------------------------------------------
// Simple stream-FIFO
// --------------------------------------------------------------------------
module SyncFIFO #(
parameter AWIDTH = 10,
parameter DWIDTH = 8
)(
input wire rstn,
input wire clk,
output wire emptyn,
input wire itvalid,
output wire itready,
input wire [DWIDTH-1:0] itdata,
output wire otvalid,
input wire otready,
output wire [DWIDTH-1:0] otdata
);
localparam [AWIDTH-1:0] ONE = 1;
reg [AWIDTH-1:0] wpt, rpt;
reg dvalid, valid;
reg [DWIDTH-1:0] datareg;
wire rreq;
wire [DWIDTH-1:0] rdata;
assign emptyn = rpt != wpt;
assign itready = rpt != (wpt+1);
assign otvalid = valid | dvalid;
assign rreq = emptyn & ( otready | ~otvalid );
assign otdata = dvalid ? rdata : datareg;
always @ (posedge clk or negedge rstn)
if(~rstn)
wpt <= 0;
else if(itvalid & itready)
wpt <= wpt + ONE;
always @ (posedge clk or negedge rstn)
if(~rstn)
rpt <= 0;
else if(rreq & emptyn)
rpt <= rpt + ONE;
always @ (posedge clk or negedge rstn)
if(~rstn) begin
dvalid <= 1'b0;
valid <= 1'b0;
datareg <= 0;
end else begin
dvalid <= rreq;
if(dvalid)
datareg <= rdata;
if(otready)
valid <= 1'b0;
else if(dvalid)
valid <= 1'b1;
end
SyncRAM #(
.DWIDTH ( DWIDTH ),
.AWIDTH ( AWIDTH )
) ram_for_fifo (
.clk ( clk ),
.wen ( itvalid ),
.waddr ( wpt ),
.wdata ( itdata ),
.raddr ( rpt ),
.rdata ( rdata )
);
endmodule
// --------------------------------------------------------------------------
// Simple Dual Port RAM
// --------------------------------------------------------------------------
module SyncRAM #(
parameter AWIDTH = 10,
parameter DWIDTH = 32
)(
input logic clk,
input logic wen,
input logic [AWIDTH-1:0] waddr,
input logic [DWIDTH-1:0] wdata,
input logic [AWIDTH-1:0] raddr,
output logic [DWIDTH-1:0] rdata
);
reg [DWIDTH-1:0] mem [(1<<AWIDTH)];
always @ (posedge clk)
if(wen)
mem[waddr] <= wdata;
always @ (posedge clk)
rdata <= mem[raddr];
endmodule

View File

@ -1,4 +1,11 @@
`timescale 1 ns/1 ns
//--------------------------------------------------------------------------------------------------------
// Module : axi_self_test_master
// Type : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// Function: write increase data to AXI4 slave,
// then read data and check whether they are increasing
//--------------------------------------------------------------------------------------------------------
module axi_self_test_master #(
parameter A_WIDTH_TEST = 26,
@ -32,10 +39,13 @@ module axi_self_test_master #(
output reg [ 15:0] error_cnt
);
initial {awaddr, araddr} = '0;
initial {error, error_cnt} = '0;
wire aw_end;
reg awaddr_carry;
reg [7:0] w_cnt;
enum logic [2:0] {INIT, AW, W, B, AR, R} stat;
reg awaddr_carry = '0;
reg [7:0] w_cnt = '0;
enum logic [2:0] {INIT, AW, W, B, AR, R} stat = INIT;
generate if(A_WIDTH_TEST<A_WIDTH)
assign aw_end = awaddr[A_WIDTH_TEST];
@ -47,12 +57,14 @@ assign awvalid = stat==AW;
assign awlen = WBURST_LEN;
assign wvalid = stat==W;
assign wlast = w_cnt==WBURST_LEN;
assign wdata = awaddr;
assign wdata = (D_WIDTH)'(awaddr);
assign bready = 1'b1;
assign arvalid = stat==AR;
assign arlen = RBURST_LEN;
assign rready = 1'b1;
wire [A_WIDTH:0] araddr_next = {1'b0,araddr} + (A_WIDTH+1)'(1<<D_LEVEL);
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
{awaddr_carry, awaddr} <= '0;
@ -72,7 +84,7 @@ always @ (posedge aclk or negedge aresetn)
stat <= W;
end
W: if(wready) begin
{awaddr_carry, awaddr} <= {awaddr_carry, awaddr} + (1<<D_LEVEL);
{awaddr_carry, awaddr} <= {awaddr_carry, awaddr} + (A_WIDTH+1)'(1<<D_LEVEL);
w_cnt <= w_cnt + 8'd1;
if(wlast)
stat <= B;
@ -84,8 +96,6 @@ always @ (posedge aclk or negedge aresetn)
stat <= R;
end
R: if(rvalid) begin
automatic logic [A_WIDTH:0] araddr_next;
araddr_next = araddr + (1<<D_LEVEL);
araddr <= araddr_next[A_WIDTH-1:0];
if(rlast) begin
stat <= AR;
@ -99,7 +109,7 @@ always @ (posedge aclk or negedge aresetn)
// ------------------------------------------------------------
// read and write mismatch detect
// ------------------------------------------------------------
wire [D_WIDTH-1:0] rdata_idle = araddr;
wire [D_WIDTH-1:0] rdata_idle = (D_WIDTH)'(araddr);
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
error <= 1'b0;

View File

@ -80,10 +80,105 @@
// DO NOT CHANGE THE TIMESCALE
// MAKE SURE YOUR SIMULATOR USE "PS" RESOLUTION
`timescale 1ns / 1ps
`timescale 1ps/1ps
module micron_ddr_sdram_model (Clk, Clk_n, Cke, Cs_n, Ras_n, Cas_n, We_n, Ba , Addr, Dm, Dq, Dqs);
`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
module ddr (Clk, Clk_n, Cke, Cs_n, Ras_n, Cas_n, We_n, Ba , Addr, Dm, Dq, Dqs);
`include "ddr_parameters.vh"
// Port Declarations
input Clk;

View File

@ -1,6 +1,20 @@
`timescale 1 ns/1 ns
module tb_ddr_ctrl();
//--------------------------------------------------------------------------------------------------------
// Module : tb_ddr_sdram_ctrl
// Type : simulation, top
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// Function: testbench for ddr_sdram_ctrl
//--------------------------------------------------------------------------------------------------------
`timescale 1ps/1ps
module tb_ddr_sdram_ctrl();
// -----------------------------------------------------------------------------------------------------------------------------
// simulation control
// -----------------------------------------------------------------------------------------------------------------------------
initial $dumpvars(0, tb_ddr_sdram_ctrl);
initial #200000000 $finish; // simulation for 200us
// -------------------------------------------------------------------------------------
// DDR-SDRAM parameters
@ -28,7 +42,7 @@ localparam D_WIDTH = (8<<DQ_LEVEL);
// driving clock and reset generate
// -------------------------------------------------------------------------------------
reg rstn=1'b0, clk300m=1'b1;
always #1.6667 clk300m = ~clk300m;
always #1667 clk300m = ~clk300m;
initial begin repeat(4) @(posedge clk300m); rstn<=1'b1; end
// -------------------------------------------------------------------------------------
@ -76,8 +90,8 @@ wire error;
// meta AXI4 master for testing
// -------------------------------------------------------------------------------------
axi_self_test_master #(
.A_WIDTH_TEST( 12 ),
.A_WIDTH ( A_WIDTH ),
.A_WIDTH_TEST( 14 ),
.D_WIDTH ( D_WIDTH ),
.D_LEVEL ( DQ_LEVEL ),
.WBURST_LEN ( WBURST_LEN ),
@ -120,7 +134,7 @@ ddr_sdram_ctrl #(
.tW2I ( 8'd6 ),
.tR2I ( 8'd6 )
) ddr_sdram_ctrl_i (
.rstn ( rstn ),
.rstn_async ( rstn ),
.clk ( clk300m ),
.aresetn ( aresetn ),
.aclk ( aclk ),
@ -157,9 +171,9 @@ ddr_sdram_ctrl #(
);
// -------------------------------------------------------------------------------------
// DDR-SDRAM simulation model
// MICRON DDR-SDRAM simulation model
// -------------------------------------------------------------------------------------
ddr ddr_i (
micron_ddr_sdram_model ddr_model_i (
.Clk ( ddr_ck_p ),
.Clk_n ( ddr_ck_n ),
.Cke ( ddr_cke ),

View File

@ -0,0 +1,5 @@
del sim.out dump.vcd
iverilog -g2005-sv -o sim.out tb_ddr_sdram_ctrl.sv axi_self_test_master.sv micron_ddr_sdram_model.v ../RTL/ddr_sdram_ctrl.sv
vvp -n sim.out
del sim.out
pause

View File

@ -1,4 +1,11 @@
`timescale 1 ns/1 ns
//--------------------------------------------------------------------------------------------------------
// Module : axi_self_test_master
// Type : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// Function: write increase data to AXI4 slave,
// then read data and check whether they are increasing
//--------------------------------------------------------------------------------------------------------
module axi_self_test_master #(
parameter A_WIDTH_TEST = 26,
@ -32,10 +39,13 @@ module axi_self_test_master #(
output reg [ 15:0] error_cnt
);
initial {awaddr, araddr} = '0;
initial {error, error_cnt} = '0;
wire aw_end;
reg awaddr_carry;
reg [7:0] w_cnt;
enum logic [2:0] {INIT, AW, W, B, AR, R} stat;
reg awaddr_carry = '0;
reg [7:0] w_cnt = '0;
enum logic [2:0] {INIT, AW, W, B, AR, R} stat = INIT;
generate if(A_WIDTH_TEST<A_WIDTH)
assign aw_end = awaddr[A_WIDTH_TEST];
@ -47,12 +57,14 @@ assign awvalid = stat==AW;
assign awlen = WBURST_LEN;
assign wvalid = stat==W;
assign wlast = w_cnt==WBURST_LEN;
assign wdata = awaddr;
assign wdata = (D_WIDTH)'(awaddr);
assign bready = 1'b1;
assign arvalid = stat==AR;
assign arlen = RBURST_LEN;
assign rready = 1'b1;
wire [A_WIDTH:0] araddr_next = {1'b0,araddr} + (A_WIDTH+1)'(1<<D_LEVEL);
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
{awaddr_carry, awaddr} <= '0;
@ -72,7 +84,7 @@ always @ (posedge aclk or negedge aresetn)
stat <= W;
end
W: if(wready) begin
{awaddr_carry, awaddr} <= {awaddr_carry, awaddr} + (1<<D_LEVEL);
{awaddr_carry, awaddr} <= {awaddr_carry, awaddr} + (A_WIDTH+1)'(1<<D_LEVEL);
w_cnt <= w_cnt + 8'd1;
if(wlast)
stat <= B;
@ -84,8 +96,6 @@ always @ (posedge aclk or negedge aresetn)
stat <= R;
end
R: if(rvalid) begin
automatic logic [A_WIDTH:0] araddr_next;
araddr_next = araddr + (1<<D_LEVEL);
araddr <= araddr_next[A_WIDTH-1:0];
if(rlast) begin
stat <= AR;
@ -99,7 +109,7 @@ always @ (posedge aclk or negedge aresetn)
// ------------------------------------------------------------
// read and write mismatch detect
// ------------------------------------------------------------
wire [D_WIDTH-1:0] rdata_idle = araddr;
wire [D_WIDTH-1:0] rdata_idle = (D_WIDTH)'(araddr);
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
error <= 1'b0;
@ -110,4 +120,4 @@ always @ (posedge aclk or negedge aresetn)
error_cnt <= error_cnt + 16'd1;
end
endmodule
endmodule

View File

@ -1,6 +1,14 @@
`timescale 1 ns/1 ns
module top(
//--------------------------------------------------------------------------------------------------------
// Module : top
// Type : synthesizable, FPGA's top, IP's example design
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// Function: an example of ddr_sdram_ctrl,
// write increase data to DDR,
// then read data and check whether they are increasing
//--------------------------------------------------------------------------------------------------------
module top (
input wire clk50m,
output wire ddr_ck_p, ddr_ck_n,
@ -75,8 +83,8 @@ pll pll_i(
// meta AXI4 master for testing
// -------------------------------------------------------------------------------------
axi_self_test_master #(
.A_WIDTH ( A_WIDTH ),
.A_WIDTH_TEST( A_WIDTH ),
.A_WIDTH ( A_WIDTH ),
.D_WIDTH ( D_WIDTH ),
.D_LEVEL ( DQ_LEVEL ),
.WBURST_LEN ( 8'd15 ),
@ -119,7 +127,7 @@ ddr_sdram_ctrl #(
.tW2I ( 8'd7 ),
.tR2I ( 8'd7 )
) ddr_ctrl_i(
.rstn ( rstn ),
.rstn_async ( rstn ),
.clk ( clk300m ),
.aresetn ( aresetn ),
.aclk ( aclk ),

View File

@ -41,7 +41,7 @@ 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 "21:02:52 JANUARY 17, 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
@ -129,11 +129,13 @@ set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_ADVANCED_TRIGGER_
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_TRIGGER_LEVEL_PIPELINE=1" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_ENABLE_ADVANCED_TRIGGER=0" -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_clk -to "ddr_ctrl:ddr_ctrl_i|clk2" -section_id auto_signaltap_0
set_global_assignment -name SYSTEMVERILOG_FILE RTL/top.sv
set_global_assignment -name SYSTEMVERILOG_FILE RTL/axi_self_test_master.sv
set_global_assignment -name VERILOG_FILE RTL/pll.v
set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/ddr_sdram_ctrl.sv
set_global_assignment -name VERILOG_FILE IP/pll.v
set_global_assignment -name SIGNALTAP_FILE SignalTap/stp1.stp
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_trigger_in[0] -to error -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[0] -to ddr_a[0] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[1] -to ddr_a[10] -section_id auto_signaltap_0
@ -173,7 +175,42 @@ set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[34] -t
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[35] -to error_cnt[8] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[36] -to error_cnt[9] -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_DATA_BITS=37" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_NODE_CRC_LOWORD=45013" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_NODE_CRC_HIWORD=22477" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_STORAGE_QUALIFIER_BITS=47" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_INCREMENTAL_ROUTING=1" -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[0] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[1] -to auto_signaltap_0|gnd -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[2] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[3] -to auto_signaltap_0|gnd -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[4] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[5] -to auto_signaltap_0|gnd -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[6] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[7] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[8] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[9] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[10] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[11] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[12] -to auto_signaltap_0|gnd -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[13] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[14] -to auto_signaltap_0|gnd -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[15] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[16] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[17] -to auto_signaltap_0|gnd -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[18] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[19] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[20] -to auto_signaltap_0|gnd -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[21] -to auto_signaltap_0|gnd -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[22] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[23] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[24] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[25] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[26] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[27] -to auto_signaltap_0|gnd -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[28] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[29] -to auto_signaltap_0|gnd -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[30] -to auto_signaltap_0|vcc -section_id auto_signaltap_0
set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[31] -to auto_signaltap_0|gnd -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_TRIGGER_PIPELINE=0" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_RAM_PIPELINE=0" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_COUNTER_PIPELINE=0" -section_id auto_signaltap_0
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top
set_global_assignment -name SLD_FILE db/stp1_auto_stripped.stp

View File

@ -1,138 +0,0 @@
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 (hex<4'hA) ? (hex+"0") : (hex+("A"-8'hA)) ;
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 <= (cyccnt<CLK_DIV-1) ? cyccnt+1 : 0;
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
fifo_rpt <= '0;
endofline <= 1'b0;
data <= '0;
uart_txb <= 1'b1;
txshift <= '1;
txcnt <= 0;
hexcnt <= 0;
end else begin
if( hexcnt>(1+TX_WIDTH) ) begin
uart_txb <= 1'b1;
endofline <= fifo_tlast;
data <= '0;
data[DATA_WIDTH-1:0] <= fifo_data;
hexcnt <= hexcnt-1;
end else if(hexcnt>0 || txcnt>0) begin
if(cyccnt==CLK_DIV-1) begin
if(txcnt>0) begin
{txshift, uart_txb} <= {1'b1, txshift};
txcnt <= txcnt-1;
end else begin
uart_txb <= 1'b0;
hexcnt <= hexcnt-1;
if(hexcnt>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<<ADDR_LEN);
logic [DATA_LEN-1:0] mem [RAM_SIZE];
initial rd_data = 0;
always @ (posedge clk)
rd_data <= mem[rd_addr];
always @ (posedge clk)
if(wr_req)
mem[wr_addr] <= wr_data;
endmodule

View File

@ -1,6 +1,13 @@
`timescale 1 ns/1 ns
module top(
//--------------------------------------------------------------------------------------------------------
// Module : top
// Type : synthesizable, FPGA's top, IP's example design
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// Function: an example of ddr_sdram_ctrl,
// use UART command to read/write DDR
//--------------------------------------------------------------------------------------------------------
module top (
input wire clk50m,
output wire uart_tx,
@ -114,7 +121,7 @@ ddr_sdram_ctrl #(
.tW2I ( 8'd7 ),
.tR2I ( 8'd7 )
) ddr_ctrl_i(
.rstn ( rstn ),
.rstn_async ( rstn ),
.clk ( clk300m ),
.aresetn ( aresetn ),
.aclk ( aclk ),

View File

@ -1,4 +1,10 @@
`timescale 1 ns/1 ns
//--------------------------------------------------------------------------------------------------------
// Module : uart2axi4
// Type : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// Function: convert UART command to AXI4 read/write action
//--------------------------------------------------------------------------------------------------------
module uart2axi4 #(
parameter A_WIDTH = 26,
@ -263,3 +269,232 @@ always @ (posedge clk)
mem[wr_addr] <= wr_data;
endmodule
module uart_rx #(
parameter CLK_DIV = 108, // UART baud rate = clk freq/(4*CLK_DIV)
// modify CLK_DIV to change the UART baud
// for example, when clk=50MHz, CLK_DIV=108, then baud=100MHz/(4*108)=115200
// 115200 is a typical baud rate for UART
parameter CLK_PART = 4 // from 0 to 7
) (
input wire clk, rstn,
// uart rx input
input wire rx,
// user interface
output wire rvalid,
output wire [7:0] rdata
);
reg done = 1'b0;
reg [ 7:0] data = 8'h0;
reg [ 2:0] supercnt=3'h0;
reg [31:0] cnt = 0;
reg [ 7:0] databuf = 8'h0;
reg [ 5:0] status=6'h0, shift=6'h0;
reg rxr=1'b1;
wire recvbit = (shift[1]&shift[0]) | (shift[0]&rxr) | (rxr&shift[1]) ;
wire [2:0] supercntreverse = {supercnt[0], supercnt[1], supercnt[2]};
assign rvalid = done;
assign rdata = data;
always @ (posedge clk or negedge rstn)
if(~rstn)
rxr <= 1'b1;
else
rxr <= rx;
always @ (posedge clk or negedge rstn)
if(~rstn) begin
done <= 1'b0;
data <= 8'h0;
status <= 6'h0;
shift <= 6'h0;
databuf <= 8'h0;
cnt <= 0;
end else begin
done <= 1'b0;
if( (supercntreverse<CLK_PART) ? (cnt>=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 <= (cyccnt<CLK_DIV-1) ? cyccnt+1 : 0;
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
fifo_rpt <= '0;
endofline <= 1'b0;
data <= '0;
uart_txb <= 1'b1;
txshift <= '1;
txcnt <= 0;
hexcnt <= 0;
end else begin
if( hexcnt>(1+TX_WIDTH) ) begin
uart_txb <= 1'b1;
endofline <= fifo_tlast;
data <= '0;
data[DATA_WIDTH-1:0] <= fifo_data;
hexcnt <= hexcnt-1;
end else if(hexcnt>0 || txcnt>0) begin
if(cyccnt==CLK_DIV-1) begin
if(txcnt>0) begin
{txshift, uart_txb} <= {1'b1, txshift};
txcnt <= txcnt-1;
end else begin
uart_txb <= 1'b0;
hexcnt <= hexcnt-1;
if(hexcnt>1)
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<<ADDR_LEN);
logic [DATA_LEN-1:0] mem [RAM_SIZE];
initial rd_data = 0;
always @ (posedge clk)
rd_data <= mem[rd_addr];
always @ (posedge clk)
if(wr_req)
mem[wr_addr] <= wr_data;
endmodule

View File

@ -1,71 +0,0 @@
module uart_rx #(
parameter CLK_DIV = 108, // UART baud rate = clk freq/(4*CLK_DIV)
// modify CLK_DIV to change the UART baud
// for example, when clk=50MHz, CLK_DIV=108, then baud=100MHz/(4*108)=115200
// 115200 is a typical baud rate for UART
parameter CLK_PART = 4 // from 0 to 7
) (
input wire clk, rstn,
// uart rx input
input wire rx,
// user interface
output wire rvalid,
output wire [7:0] rdata
);
reg done = 1'b0;
reg [ 7:0] data = 8'h0;
reg [ 2:0] supercnt=3'h0;
reg [31:0] cnt = 0;
reg [ 7:0] databuf = 8'h0;
reg [ 5:0] status=6'h0, shift=6'h0;
reg rxr=1'b1;
wire recvbit = (shift[1]&shift[0]) | (shift[0]&rxr) | (rxr&shift[1]) ;
wire [2:0] supercntreverse = {supercnt[0], supercnt[1], supercnt[2]};
assign rvalid = done;
assign rdata = data;
always @ (posedge clk or negedge rstn)
if(~rstn)
rxr <= 1'b1;
else
rxr <= rx;
always @ (posedge clk or negedge rstn)
if(~rstn) begin
done <= 1'b0;
data <= 8'h0;
status <= 6'h0;
shift <= 6'h0;
databuf <= 8'h0;
cnt <= 0;
end else begin
done <= 1'b0;
if( (supercntreverse<CLK_PART) ? (cnt>=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

View File

@ -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

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@ -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