first commit

This commit is contained in:
WangXuan95 2021-01-27 18:06:35 +08:00
commit a528872406
25 changed files with 23837 additions and 0 deletions

454
README.md Normal file
View File

@ -0,0 +1,454 @@
![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)
FPGA DDR-SDRAM
===========================
在低端FPGA设计中用 DDR-SDRAMDDR1替换 SDR-SDRAM。
# 简介
|-------------------------| |--------------|
| | | |
----->| 驱动时钟和复位 | | |
| | | |
------------| | DDR-SDRAM接口 |--------->| |
| AXI4 | | | |
AXI4 master |----------->| AXI4 slave | | |
| | | | |
------------| |-------------------------| |--------------|
用户逻辑 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](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/RTL/ddr_sdram_ctrl.sv) )。该控制器的特点有:
* **平台无关** :纯 RTL 编写,可以在 Altera 和 Xilinx 等各种 FPGA 上运行。
* **兼容性强** :支持各种位宽和容量的 DDR1 已在MICRON所有位宽和容量的DDR1型号上仿真通过
为了展示该控制器的使用方法,我提供了两个示例程序:
* **自测程序**通过DDR1控制器将数据写入 DDR1 ,然后读出,比较读出的数据是否与写入的吻合。
* **UART读写程序**:将 UART 命令转化成 AXI4 总线动作通过DDR1控制器读写DDR1。
另外,由于各代 DDR-SDRAM例如DDR3、DDR2、DDR1的接口时序大同小异本库也可以方便那些熟悉 Verilog 的人来学习 DDR-SDRAM 接口。
# 目录
* [简介](#简介)
* [硬件设计指南](#硬件设计指南)
* [硬件设计示例](#硬件设计示例)
* [DDR1 控制器](#DDR1 控制器)
* [模块参数](#模块参数)
* [模块接口 - 驱动时钟和复位](#模块接口 - 驱动时钟和复位)
* [时钟频率的选取](#时钟频率的选取)
* [模块接口 - DDR1 接口](#模块接口 - DDR1 接口)
* [模块接口 - AXI4 从接口](#模块接口 - AXI4 从接口)
* [位宽参数的确定](#位宽参数的确定)
* [时序参数的确定](#时序参数的确定)
* [示例程序](#示例程序)
* [示例程序 - 自测](#示例程序 - 自测)
* [示例程序 - 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 。
下表展示了 DDR1 颗粒 的引脚,以及其与 FPGA 连接时应该注意的点。
| 引脚名称 | 方向 | 位宽 | 说明 | 连接方法 | 注意事项 |
| :---: | :---: | :---: | :--- | :--- | :--- |
| ddr_ck_p | FPGA output | 1 | 差分时钟正≥75MHz | 应与 FPGA 的 2.5V LVTTL管脚连接 | 布线尽量短,与 ddr_ck_n 差分布线 |
| ddr_ck_n | FPGA output | 1 | 差分时钟负≥75MHz | 应与 FPGA 的 2.5V LVTTL管脚连接 | 布线尽量短,与 ddr_ck_p 差分布线 |
| ddr_cke、ddr_cs_n | FPGA output | 1 | 低速 | 应与 FPGA 的 2.5V LVTTL管脚连接 | |
| ddr_ras_n、ddr_cas_n、ddr_we_n | FPGA output | 1 | 与 ddr_ck_p 下降沿同步 | 应与 FPGA 的 2.5V LVTTL管脚连接 | 布线尽量短,与 ddr_ck_p 大致等长(长度别太离谱即可) |
| ddr_ba | FPGA output | 2 | 与 ddr_ck_p 下降沿同步 | 应与 FPGA 的 2.5V LVTTL管脚连接 | 布线尽量短,与 ddr_ck_p 大致等长(别太离谱即可) |
| ddr_a | FPGA output | 取决于芯片型号 | 与 ddr_ck_p 下降沿同步 | 应与 FPGA 的 2.5V LVTTL管脚连接 | 布线尽量短,与 ddr_ck_p 大致等长(别太离谱即可) |
| ddr_dqs | inout | 取决于芯片型号 | | 应与 FPGA 的 2.5V LVTTL管脚连接 | 布线尽量短 |
| ddr_dm、ddr_dq | inout | 取决于芯片型号 | 与 ddr_dqs 双沿同步 | 应与 FPGA 的 2.5V LVTTL管脚连接 | 布线尽量短,与 ddr_dqs 大致等长(别太离谱即可) |
# 硬件设计示例
我用 Altera Cyclone IV 的一个非常低端和廉价的 FPGA 型号EP4CE6E22C8N) 和 MICRON 的 64MB DDR1 (型号 MT46V64M8TG-6T画了一个小板子本库的所有例子可以直接在该板子上直接运行。
| ![board-image](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/images/board.jpg) |
| :---: |
| 图FPGA + DDR1 测试板 |
原理图见 [hardware/sch.pdf](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/hardware/sch.pdf)PCB制造文件见 [hardware/gerber.zip](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/hardware/gerber.zip) ,在布局布线时,使用双层板即可,不需像 DDR2 以上的设计那样刻意注意等长和阻抗匹配,因为该电路工作频率为双边沿 75MHz不是特别高只需注意让FPGA与DDR距离尽量近布线尽量短即可。比如我在布局时把DDR1芯片放在了FPGA芯片正对的背面从而保证布线都较短。
该板子的设计在立创EDA中开放见[这里](https://oshwhub.com/wangxuan/fpga-ddr-ce-shi-ban)。
# DDR1 控制器
DDR1 控制器代码见 [RTL/ddr_sdram_ctrl.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/RTL/ddr_sdram_ctrl.sv) ,是一个用 SystemVerilog 编写的模块,它能自动对 DDR1 进行初始化并定时进行刷新Refresh。该模块有一个简化但完备的 AXI4 从接口,通过它可以完成对 DDR1 的读写。本节详细解释该模块的使用方法。
## 模块参数
模块参数和默认值在 Verilog 中定义如下:
```Verilog
module ddr_sdram_ctrl #(
parameter READ_BUFFER = 1,
parameter BA_BITS = 2,
parameter ROW_BITS = 13,
parameter COL_BITS = 11,
parameter DQ_LEVEL = 1,
parameter [9:0] tREFC = 10'd256,
parameter [7:0] tW2I = 8'd7,
parameter [7:0] tR2I = 8'd7
)
```
对这些参数说明如下:
| 参数名 | 类型 | 取值范围 | 默认值 | 说明 |
| :--: | :--: | :--: | :--: | :-- |
| READ_BUFFER | 配置参数 | 0 或 1 | 1 | 若设为0DDR1控制器内将没有读数据缓冲读出的数据将不会等待AXI4主机是否能接受即rvalid信号不会等待rready信号直接以最高速率灌出去此时AXI4是不完备的但可以降低读出的时延。若设为1则DDR1控制器内会有一个足够大的读数据缓冲rvalid信号会与rready信号握手确认AXI4主机准备好后再读出数据。 |
| BA_BITS | 位宽参数 | 1~4 | 2 | 规定了 DDR BANK ADDRESS (ddr_ba) 的宽度,常规的 DDR1 的 BANK ADDRESS 都是 2bit因此该参数通常固定为默认值不用改 |
| ROW_BITS | 位宽参数 | 1~15 | 13 | 规定了 DDR ROW ADDRESS 的宽度,同时也决定了 DDR1 芯片的地址线引脚(ddr_a)的宽度。该参数取决于DDR1芯片的选型。例如 MT46V64M8 每个 BANK 有 8192 个 COL考虑到2^11=8192该参数应为13。同理对于 MT46V128M8该参数应为14 |
| COL_BITS | 位宽参数 | 1~14 | 11 | 规定了 DDR COL ADDRESS 的宽度。该参数取决于DDR1芯片的选型。例如 MT46V64M8 每个 ROW 有 2048 个 COL考虑到2^11=2048因此该参数应该为11。同理对于MT46V32M16该参数应为10。 |
| DQ_LEVEL | 位宽参数 | 0~7 | 1 | 规定了 DDR1 芯片的数据位宽对于位宽x4的芯片例如MT46V64M4该参数应取0对于位宽x8的芯片例如MT46V64M8该参数应取1对于位宽x16的芯片例如MT46V64M16该参数应取2对于位宽x32的情况例如两片MT46V64M16扩展位宽该参数应取3以此类推。
| tREFC | 时序参数 | 10'd1 ~ 10'd1023 | 10'd256 | 该控制器会周期性地刷新 DDR1该参数规定了刷新的间隔。详见下文[时序参数的确定](#时序参数的确定) |
| tW2I | 时序参数 | 8'd1~8'd255 | 8'd7 | 该参数规定了一个写动作的最后一个写命令到下一个动作的激活命令(ACT)的间隔。详见下文[时序参数的确定](#时序参数的确定) |
| tR2I | 时序参数 | 8'd1~8'd255 | 8'd7 | 该参数规定了一个读动作的最后一个读命令到下一个动作的激活命令(ACT)的间隔。详见下文[时序参数的确定](#时序参数的确定) |
## 模块接口 - 驱动时钟和复位
该模块需要一个时钟和一个复位进行驱动,如下:
```Verilog
input wire rstn,
input wire clk,
```
rstn 是低电平复位信号正常工作时应该置高。clk 是驱动时钟,频率是用户时钟的 4 倍。
模块开始工作前rstn信号应该置低让模块复位当 clk 的频率稳定后,可以把 rstn 置高,让模块处于工作状态。
如果 clk 一开始就是稳定的(例如直接来自晶振输入),那么 rstn 可以直接置为 1'b1 。
如果 clk 经过一段时间后才能稳定(例如来自 FPGA 的 PLL 或 MMCM那么 rstn 应该接 PLL/MMCM 的 locked 信号,保证 PLL 锁相成功(稳定)后,模块再开始工作。
## 时钟频率的选取
模块内使用寄存器分频 4 倍产生 DDR1 驱动时钟ddr_ck_p/ddr_ck_n和 AXI4 总线用户时钟aclk。本节讲述如何决定驱动时钟 clk 的频率。
首先,时钟频率受限于 DDR1 芯片。考虑到所有的 DDR1 的接口频率至少为 75MHz ,则 clk 的下限是 75\*4=300MHz。而 clk 的上限就取决于 DDR1 的芯片型号,例如对于 MT46V64M8P-5B ,查芯片手册可知,-5B 后缀的 DDR1 在 CL=1 时最高时钟频率是 133MHz则 clk 的上限是 133\*4=532MHz 。
另外,时钟频率的上限还受限于 FPGA 的速度,太高的时钟频率容易导致时序不收敛。该控制器充分考虑时序安全设计,大多数寄存器工作在频率较低用户时钟域;个别寄存器工作在用户时钟的 2 倍频率的时钟下,且输入端口的组合逻辑非常短;还有一个寄存器工作在高频的 clk 下,但输入端口直接来自于上一级的寄存器输出(没有组合逻辑)。因此,即使在速度级别很低的 EP4CE6E22C8N 上,在 300MHz 的驱动时钟下也能保证模块正确运行。在速度等级更高的 FPGA (例如 EP4CE22F17C6N驱动时钟的频率可以更高例如400MHz
> 注意:本控制器固定 CAS Latency (CL) = 2。
## 模块接口 - DDR1 接口
以下是该模块的 DDR1 接口。这些接口应该直接从 FPGA 引出,连接到 DDR1 芯片上。
```Verilog
output wire ddr_ck_p, ddr_ck_n,
output wire ddr_cke,
output reg ddr_cs_n,
output reg ddr_ras_n,
output reg ddr_cas_n,
output reg ddr_we_n,
output reg [ BA_BITS-1:0] ddr_ba,
output reg [ ROW_BITS-1:0] ddr_a,
output wire [((1<<DQ_LEVEL)+1)/2-1:0] ddr_dm,
inout [((1<<DQ_LEVEL)+1)/2-1:0] ddr_dqs,
inout [ (4<<DQ_LEVEL)-1:0] ddr_dq
```
可以看出 DDR1 接口的一些信号的位宽是和参数有关的,用户需要根据 DDR1 的芯片选型来确定模块参数。详见 [位宽参数的确定](#位宽参数的确定)。
想了解 DDR1 接口在初始化、读写、刷新时的波形,请进行 [Verilog 仿真](#Verilog 仿真)。
## 模块接口 - AXI4 从接口
DDR1 控制器对外提供 AXI4 从接口AXI4 slave的时钟和复位信号如下。AXI4 主机应该用它们作为自己的时钟和复位。
```Verilog
output reg aresetn,
output reg aclk,
```
以下是该模块的 AXI4 从接口,它们都与 aclk 时钟的上升沿同步,应该连接到 FPGA 内的的 AXI4 主机。
```Verilog
input wire awvalid,
output wire awready,
input wire [BA_BITS+ROW_BITS+COL_BITS+DQ_LEVEL-2:0] awaddr,
input wire [ 7:0] awlen,
input wire wvalid,
output wire wready,
input wire wlast,
input wire [(8<<DQ_LEVEL)-1:0] wdata,
output wire bvalid,
input wire bready,
input wire arvalid,
output wire arready,
input wire [BA_BITS+ROW_BITS+COL_BITS+DQ_LEVEL-2:0] araddr,
input wire [ 7:0] arlen,
output wire rvalid,
input wire rready,
output wire rlast,
output wire [(8<<DQ_LEVEL)-1:0] rdata,
```
AXI4 总线的数据wdata和rdata的位宽是 DDR1 数据位宽的 2 倍。例如,对于位宽为 8bit 的 MT46V64M8wdata 和 rdata 的位宽是 16bit 。
AXI4 总线的地址awaddr和araddr统一是字节地址模块会根据用户指定的参数来计算 awaddr 和 araddr 的位宽 = BA_BITS+ROW_BITS+COL_BITS+DQ_LEVEL-1 。例如对于 64MB 的 MT46V64M8 awaddr/araddr 位宽为 26而 2^26 等于 64MB。
每次读写访问时地址值awaddr和araddr都要和整个字对齐例如若 wdata 和 rdata 的位宽是 16bit2字节那么 wdata 和 rdata 要按 2 字节对齐,即能整除 2。
该模块的 AXI4 接口不支持字节选通写入,没有 wstrb 信号,每次至少写入一整个数据位宽,例如若 wdata 的位宽是 16那么一次至少写入2字节。
该模块的 AXI4 接口支持突发读写,突发长度可取 1~256 之间的任意值,即 awlen 和 arlen 取值范围为 0~255 。例如在一次写入动作中awlen=12则突发长度为 13若 wdata 的位宽是 16 ,那么该次突发写一共写了 13\*2=26B。
另外,每次突发读写不能超越 DDR1 内的一个行的边界。例如对于 MT46V64M8每个行有 8\*2^11/8 = 2048B那么每次突发读写就不能超越 2048B 的边界。
### AXI4 写操作时序
__ __ __ __ __ __ __ __ __ __ __ __ _
aclk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/
___________
awvalid ___/ \____________________________________________________________
_____
awready _________/ \____________________________________________________________
___________
awaddr XXXX____ADDR___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
___________
awlen XXXX____8'd3___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
___________ ___________ _____
wvalid _______________/ \_____/ \_____/ \__________________
___________________________________
wready _____________________/ \__________________
_____
wlast ___________________________________________________/ \__________________
___________ _____ _____ _____
wdata XXXXXXXXXXXXXXXX_____D0____XXXXXXX__D1_X__D2_XXXXXXX__D3_XXXXXXXXXXXXXXXXXXX
___________
bvalid _________________________________________________________/ \______
_____
bready _______________________________________________________________/ \______
一个典型的,突发长度为 4 awlen=3 的 AXI4 写操作如上图。分为 3 步:
* **地址通道握手**AXI4 主机把 awvalid 信号拉高指示想要发起写动作图中经过一周期后DDR1 控制器才把 awready 信号拉高说明地址通道握手成功在此之前DDR1 控制器可能在处理上一次读写动作,或者在进行刷新,因此暂时没把 awready 拉高。握手成功的同时DDR1 控制器收到待写地址awaddr和突发长度awlenawlen=8'd3 说明突发长度为 4。
* **数据传送**AXI4 总线上的 wvalid 和 wready 进行 4 次握手,把突发传输的 4 个数据写入 DDR1。在握手时若 AXI4 主机还没准备好数据,可以把 wvalid 拉低,若 DDR1 控制器还没准备好接受数据,会把 wready 拉低。注意,根据 AXI4 总线的规定,在突发传输的最后一个数据传输的同时需要把 wlast 信号置高实际上即使不置高DDR1 控制器也能根据突发长度自动结束数据传送,进入写响应状态)。
* **写响应**数据传送结束后DDR1 控制器还需要和 AXI4 主机进行一次握手才能彻底结束本次写动作。DDR1 控制器在数据传送结束后立即把 wvalid 信号拉高,并等待 wready 为高。完成该握手后DDR1才能响应下一次读写操作。
### AXI4 读操作时序
__ __ __ __ __ __ __ __ __ __ __ __ _
aclk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/
___________
arvalid ___/ \____________________________________________________________
_____
arready _________/ \____________________________________________________________
___________
araddr XXXX____ADDR___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
___________
arlen XXXX____8'd4___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
___________________________________
rvalid _________________________________/ \______
___________________________________________________ __________________
rready \_____/
_____
rlast _______________________________________________________________/ \______
_____ _____ _____ ___________ _____
rdata XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX__D0_X__D1_X__D2_X_____D3____X__D4_XXXXXXX
一个典型的,突发长度为 5 arlen=4 的 AXI4 读操作如上图。分为 2 步:
* **地址通道握手**AXI4 主机把 arvalid 信号拉高指示想要发起写动作图中经过一周期后DDR1 控制器才把 arready 信号拉高说明地址通道握手成功在此之前DDR1 控制器可能在处理上一次读写动作,或者在进行刷新,因此暂时没把 arready 拉高。握手成功的同时DDR1 控制器收到待读地址araddr和突发长度arlenarlen=8'd4 说明突发长度为 5。
* **数据传送**AXI4 总线上的 rvalid 和 rready 进行 5 次握手,把突发传输的 5 个读出。在握手时,若 AXI4 主机还没准备好接受数据,可以把 rready 拉低,则 DDR1 控制器会保持当前数据,直到 AXI4主机能接受此数据即把rready拉高为止。在传送最后一个数据时DDR1 控制器会把 rlast 拉高。
> 注:当模块参数 READ_BUFFER = 0 时,模块将节省一个 BRAM 资源,同时还能降低地址通道握手和数据传送之间的延迟。但 DDR1 控制器会忽略 rready=0 的情况,不等待 AXI4 主机是否准备好接受数据。这样将会破坏 AXI4 协议的完备性,但在一些简单的场合或许有用。
## 位宽参数的确定
本节讲述如何确定 **BA_BITS**、**ROW_BITS**、**COL_BITS** 和 **DQ_LEVEL** 这 4 个参数。
从[模块接口 - DDR1 接口](#模块接口 - DDR1 接口)一节可以看出 DDR1 接口的一些信号的位宽是和参数有关的,用户需要根据 DDR1 的芯片选型来确定模块参数。
以 [MICRON 公司的 DDR-SDRAM](https://www.micron.com/products/dram/ddr-sdram) 系列芯片为例,不同芯片有不同的 ROW ADDRESS BITS (行地址宽度、COL ADDRESS BITS列地址宽度、DATA BITS数据宽度决定了他们的位宽参数也不同如下表这些参数都能从芯片datasheet中查到
| 芯片名称 | ddr_dq 位宽 | ddr_dm 和 ddr_dqs 位宽 | DQ_LEVEL 取值 | BA_BITS | ROW_BITS | COL_BITS | 每行字节数 | 容量 | awaddr/araddr 位宽 |
| :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :-- | :--: |
| MT46V64M4 | 4 | 1 | 0 | 2 | 13 | 11 | 1024 | 4\*2^(2+13+11)=256Mb=32MB | 25 |
| MT46V128M4 | 4 | 1 | 0 | 2 | 13 | 12 | 2048 | 4\*2^(2+13+12)=512Mb=64MB | 26 |
| MT46V256M4 | 4 | 1 | 0 | 2 | 14 | 12 | 4096 | 4\*2^(2+14+12)=1Gb=128MB | 27 |
| MT46V32M8 | 8 | 1 | 1 | 2 | 13 | 10 | 1024 | 8\*2^(2+13+10)=256Mb=32MB | 25 |
| MT46V64M8 | 8 | 1 | 1 | 2 | 13 | 11 | 2048 | 8\*2^(2+13+11)=512Mb=64MB | 26 |
| MT46V128M8 | 8 | 1 | 1 | 2 | 14 | 11 | 4096 | 8\*2^(2+14+11)=1Gb=128MB | 27 |
| MT46V16M16 | 16 | 2 | 2 | 2 | 13 | 9 | 1024 | 16\*2^(2+13+9)=256Mb=32MB | 25 |
| MT46V32M16 | 16 | 2 | 2 | 2 | 13 | 10 | 2048 | 16\*2^(2+13+10)=512Mb=64MB | 26 |
| MT46V64M16 | 16 | 2 | 2 | 2 | 14 | 10 | 4096 | 16\*2^(2+14+10)=1Gb=128MB | 27 |
## 时序参数的确定
本节讲述如何确定 **tREFC**、**tW2I** 和 **tR2I** 这 3 个时序参数。
我们知道DDR1 需要周期性的刷新动作,**tREFC** 就规定了刷新的时钟周期间隔(以 aclk为准。例如若用户时钟为 75MHz根据 MT46V64M8 的芯片手册,需要至多 7.8125us 刷新一次,考虑到 75MHz * 7.8125us = 585.9,则该参数可以设置为一个小于 585 的值,例如 10'd512。
**tW2I** 规定了一个写操作的最后一个写命令到下一个操作的激活命令(ACT)的时钟周期数(以 aclk 为准)的最小值。下图展示了一个 DDR1 接口上的写操作ddr_cas_n 的第一个上升沿代表了一个写操作的最后一个写命令结束ddr_ras_n 的第二个下降沿代表了下一个操作(可能是读、写、刷新)的开始,它们之间有 5 个时钟周期,**tW2I** 就是用来规定该周期数的下限的。**tW2I** 的默认值 8'd7 是一个兼容绝大多数 DDR1 的保守值,对于不同的 DDR1 芯片,有不同的缩小的余地(详见 DDR1 芯片 datasheet
__ __ __ __ __ __ __ __ __ __ __ __
ddr_ck_p __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__
_____ _______________________________________________ ________
ddr_ras_n \_____/ \_____/
_________________ ____________________________________________
ddr_cas_n \___________/
_________________ ____________________________________________
ddr_we_n \___________/
_____
ddr_a[10] XXXXXXXXXXXXXXXXXX_____/ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
_______________________
ddr_ba XXXXXX__________BA___________XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
_______________________
ddr_a XXXXXX__RA_XXXXXXX_CA0_X_CA1_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
**tR2I** 规定了一个读操作的最后一个读命令到下一个操作的激活命令(ACT)的时钟周期数(以 aclk 为准)的最小值。下图展示了一个 DDR1 接口上的读操作ddr_cas_n 的第一个上升沿代表了一个读操作的最后一个读命令结束ddr_ras_n 的第二个下降沿代表了下一个操作(可能是读、写、刷新)的开始,它们之间有 5 个时钟周期,**tR2I** 就是用来规定该周期数的下限的。**tR2I** 的默认值 8'd7 是一个兼容绝大多数 DDR1 的保守值,对于不同的 DDR1 芯片,有不同的缩小的余地(详见 DDR1 芯片 datasheet
__ __ __ __ __ __ __ __ __ __ __ __
ddr_ck_p __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__
_____ _______________________________________________ ________
ddr_ras_n \_____/ \_____/
_________________ ____________________________________________
ddr_cas_n \___________/
__________________________________________________________________________
ddr_we_n
_____
ddr_a[10] XXXXXXXXXXXXXXXXXX_____/ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
_______________________
ddr_ba XXXXXX__________BA___________XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
_______________________
ddr_a XXXXXX__RA_XXXXXXX_CA0_X_CA1_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# 示例程序
## 示例程序 - 自测
我基于我画的 [FPGA+DDR1测试板](#硬件设计示例) 做了一个 DDR1 读写自测程序,工程目录是 [example-selftest](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/example-selftest),请用 Quartus II 13.1 打开它。
该工程包含以下文件:
| 文件名称 | 用途 |
| :---- | :--- |
| [top.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/example-selftest/RTL/top.sv) | 顶层 |
| [axi_self_test_master.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/example-selftest/RTL/axi_self_test_master.sv) | 是 AXI4 主机,通过 AXI4 先把有规律的数据写入 DDR1然后读回比较读回的数据是否符合规律并对不匹配的情况进行计数。 |
| [ddr_sdram_ctrl.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/RTL/ddr_sdram_ctrl.sv) | DDR1 控制器 |
| [pll.v](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/example-selftest/IP/pll.v) | 使用板上 50MHz 晶振产生 300MHz 时钟,输出给 DDR1 控制器 |
**写入**:该工程开始运行后,会先通过 AXI4 把整个 DDR1 都写一遍,直接把地址字写入相应的地址。例如,如果 AXI4 的数据宽度是 16bit那么地址 0x000002 处写 0x0001。地址 0x123456 处写 0x3456。
**读取&错误校验**:写完整个 DDR1 后,该工程会一轮一轮地反复读取整个 DDR1若读出的数据不符合上述规律就认为出现错误在错误信号error上产生一个高电平脉冲并把错误计数信号error_cnt+1。如果 DDR1 配置正确,是不该出现 error 信号的。你可以测量 error_cnt 对应的引脚若为0全低电平说明不存在错误。
**SignalTap抓波形**:该工程包含一个 SignalTap 文件([stp1.stp]()),在程序运行时,可以用它查看 DDR1 接口上的波形。它以 error=1 为触发信号,因此如果读写自测没有出错,它就不会被触发。因为该工程随时都在读取 DDR1要想看 DDR1 接口上的波形,直接按“停止”按钮即可。
**修改 AXI4 突发长度**:在 [top.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/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 之间的任意突发长度的。
> WBURST_LEN 和 RBURST_LEN 可以设置的不一样。
## 示例程序 - UART读写
我基于我画的 [FPGA+DDR1测试板](#硬件设计示例) 做了一个 UART 读写程序,使用该程序,你可以通过 UART 命令,以不同的突发长度来读写 DDR1。工程目录是 [example-uart-read-write](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/example-uart-read-write),请用 Quartus II 13.1 打开它。
该工程包含以下文件:
| 文件名称 | 用途 |
| :---- | :--- |
| [top.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/example-uart-read-write/RTL/top.sv) | 顶层 |
| [uart2axi4.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/example-uart-read-write/RTL/uart2axi4.sv) | 是 AXI4 主机,能把 UART RX 收到的命令转换成 AXI4 读写操作,并把读操作读出的数据通过 UART TX 发送出去 |
| [uart_rx.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/example-uart-read-write/RTL/uart_rx.sv) | UART RX 接收器,被 uart2axi4.sv 调用 |
| [axis2uarttx.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/example-uart-read-write/RTL/axis2uarttx.sv) | UART TX 发送器,被 uart2axi4.sv 调用 |
| [ddr_sdram_ctrl.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/RTL/ddr_sdram_ctrl.sv) | DDR1 控制器 |
| [pll.v](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/example-uart-read-write/IP/pll.v) | 使用板上 50MHz 晶振产生 300MHz 时钟,输出给 DDR1 控制器 |
[FPGA+DDR1测试板](#硬件设计示例)上有一个 CH340E 芯片USB 转 UART因此插上 USB 线后就可以在电脑上看见 UART 对应的 COM 口(需要先在[这里](http://www.wch.cn/product/CH340.html)下载安装 CH341 的驱动)。
工程上传 FPGA 后,双击打开我编写的一个串口小工具 [UartSession.exe](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/UartSession.exe) ,根据提示打开板子对应的 COM 口,然后打如下的命令+回车,可以把 0x0123 0x4567 0x89ab 0xcdef 这 4 个数据写入起始地址0x12345。AXI4总线上会产生一个突发长度为 4 的写操作)。
w12345 0123 4567 89ab cdef
然后用以下命令+回车,可以以 0x12345 为起始地址,以 7 为突发长度,读取 8 个数据。
r12345 7
效果如下图前4个数据 (0123 4567 89ab cdef) 就是我们已经写入 DDR1 的后4个数据我们没写过是 DDR1 初始化后自带的随机数据。
| ![UART读写](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/images/UartSession.png) |
| :--: |
| 图:使用 UartSession.exe 测试 DDR1 读写 |
写命令里有多少数据,写突发长度就是多少,例如以下写命令的突发长度是 9将 10 个数据写入起始地址 0x00000
w0 1234 2345 3456 4567 5678 6789 789a 89ab 9abc abcd
读命令则直接指定突发长度,例如以下命令的突发长度为 30 0x1e从起始地址 0x00000 将 31 个数据读出
r0 1e
# Verilog 仿真
## 建立仿真工程
仿真所需要的文件在目录 [sim-selftest](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest) 里,你可以用以下 Verilog 文件建立仿真工程:
| 文件路径 | 用途 |
| :---- | :--- |
| [sim-selftest/SRC/tb_ddr_ctrl.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/SRC/tb_ddr_ctrl.sv) | 仿真顶层 |
| [sim-selftest/SRC/axi_self_test_master.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/SRC/axi_self_test_master.sv) | 是 AXI4 主机,通过 AXI4 先把有规律的数据写入 DDR1然后读回比较读回的数据是否符合规律并对不匹配的情况进行计数。 |
| [RTL/ddr_sdram_ctrl.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/RTL/ddr_sdram_ctrl.sv) | DDR1 控制器 |
| [sim-selftest/DDR_MODEL/ddr.v](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/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](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/DDR_MODEL/ddr_parameters.vh) | DDR1 仿真模型的参数配置文件 |
该仿真工程的行为类似[自测程序](#示例程序 - 自测)[axi_self_test_master.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/SRC/axi_self_test_master.sv) 作为 AXI4 主机,将有规律的数据写入 DDR1 中,只不过不是全部写入,而是只写入 DDR1 的前 16KB (因为仿真模型的存储空间有限),然后一轮一轮地反复读出数据,比较是否有不匹配的数据,若有,则在 error 信号上产生一个时钟周期的高电平。
## 运行仿真
建立工程后,直接运行仿真,得到如下波形。前 294usAXI4 主机在进行前 16KB 的写入294us 之后AXI4 主机在不断的读出数据。error 信号一直为低电平说明读出的数据无误。
| ![仿真波形](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/images/sim.png) |
| :--: |
| 图:仿真波形 |
你可以展开查看 AXI4 总线和 DDR1 接口的波形细节。这里不做赘述。
## 修改仿真参数
以上仿真默认配置的参数是使用 MT46V64M8 ,即 ROW_BITS=13COL_BITS=11DQ_BITS=8。如果想对其它型号的 DDR1 芯片进行仿真,需要修改的参数如下:
* 仿真顶层中的 **BA_BITS**:见 [sim-selftest/SRC/tb_ddr_ctrl.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/SRC/tb_ddr_ctrl.sv) 第 8 行。
* DDR1 模型中的 **BA_BITS**:见 [sim-selftest/DDR_MODEL/ddr_parameters.vh](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/DDR_MODEL/ddr_parameters.vh) 第 35 行。
* 仿真顶层中的 **ROW_BITS**:见 [sim-selftest/SRC/tb_ddr_ctrl.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/SRC/tb_ddr_ctrl.sv) 第 9 行。
* DDR1 模型中的 **ROW_BITS**:见 [sim-selftest/DDR_MODEL/ddr_parameters.vh](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/DDR_MODEL/ddr_parameters.vh) 第 36 行。
* 仿真顶层中的 **COL_BITS**:见 [sim-selftest/SRC/tb_ddr_ctrl.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/SRC/tb_ddr_ctrl.sv) 第 10 行。
* DDR1 模型中的 **COL_BITS**:见 [sim-selftest/DDR_MODEL/ddr_parameters.vh](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/DDR_MODEL/ddr_parameters.vh) 第 37 行。
* 仿真顶层中的 **DQ_LEVEL**:见 [sim-selftest/SRC/tb_ddr_ctrl.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/SRC/tb_ddr_ctrl.sv) 第 11 行。
* DDR1 模型中的 **DQ_BITS**:见 [sim-selftest/DDR_MODEL/ddr_parameters.vh](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/DDR_MODEL/ddr_parameters.vh) 第 38 行。
对于 MICRON 公司的 DDR1 系列,这些参数应该这样修改:
| 芯片名称 | BA_BITS | ROW_BITS | COL_BITS | DQ_LEVEL | DQ_BITS
| :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: |
| MT46V64M4 | 2 | 13 | 11 | 0 | 4 |
| MT46V128M4 | 2 | 13 | 12 | 0 | 4 |
| MT46V256M4 | 2 | 14 | 12 | 0 | 4 |
| MT46V32M8 | 2 | 13 | 10 | 1 | 8 |
| MT46V64M8 | 2 | 13 | 11 | 1 | 8 |
| MT46V128M8 | 2 | 14 | 11 | 1 | 8 |
| MT46V16M16 | 2 | 13 | 9 | 2 | 16 |
| MT46V32M16 | 2 | 13 | 10 | 2 | 16 |
| MT46V64M16 | 2 | 14 | 10 | 2 | 16 |
另外,你可以修改 [tb_ddr_ctrl.sv](https://github.com/WangXuan95/FPGA-DDR-SDRAM/blob/main/sim-selftest/SRC/tb_ddr_ctrl.sv) 的第 18 和 19 行来修改仿真时的突发读写的长度。
# 参考资料
* MICRON 公司提供的 DDR1 仿真模型: https://www.micron.com/products/dram/ddr-sdram/part-catalog/mt46v64m8p-5b
* MT46V64M8 芯片手册: https://media-www.micron.com/-/media/client/global/documents/products/data-sheet/dram/ddr1/512mb_ddr.pdf?rev=4e1e995d6d2240e293286770f193d57d

593
RTL/ddr_sdram_ctrl.sv Normal file
View File

@ -0,0 +1,593 @@
`timescale 1 ns/1 ns
module ddr_sdram_ctrl #(
parameter READ_BUFFER = 1,
parameter BA_BITS = 2,
parameter ROW_BITS = 13,
parameter COL_BITS = 11,
parameter DQ_LEVEL = 1, // DDR DQ_BITS = 4<<DQ_LEVEL, AXI4 DATA WIDTH = 8<<DQ_LEVEL, for example:
// DQ_LEVEL = 0: DQ_BITS = 4 (x4) , AXI DATA WIDTH = 8
// DQ_LEVEL = 1: DQ_BITS = 8 (x8) , AXI DATA WIDTH = 16 (default)
// DQ_LEVEL = 2: DQ_BITS = 16 (x16) , AXI DATA WIDTH = 32
parameter [9:0] tREFC = 10'd256,
parameter [7:0] tW2I = 8'd7,
parameter [7:0] tR2I = 8'd7
) (
// driving clock and reset
input wire rstn,
input wire clk, // driving clock, typically 300~532MHz
// user interface ( meta AXI4 )
output reg aresetn,
output reg aclk, // freq = F(clk)/4
input wire awvalid,
output wire awready,
input wire [BA_BITS+ROW_BITS+COL_BITS+DQ_LEVEL-2:0] awaddr, // byte address, not word address.
input wire [ 7:0] awlen,
input wire wvalid,
output wire wready,
input wire wlast,
input wire [(8<<DQ_LEVEL)-1:0] wdata,
output wire bvalid,
input wire bready,
input wire arvalid,
output wire arready,
input wire [BA_BITS+ROW_BITS+COL_BITS+DQ_LEVEL-2:0] araddr, // byte address, not word address.
input wire [ 7:0] arlen,
output wire rvalid,
input wire rready,
output wire rlast,
output wire [(8<<DQ_LEVEL)-1:0] rdata,
// DDR-SDRAM interface
output wire ddr_ck_p, ddr_ck_n, // freq = F(clk)/4
output wire ddr_cke,
output reg ddr_cs_n,
output reg ddr_ras_n,
output reg ddr_cas_n,
output reg ddr_we_n,
output reg [ BA_BITS-1:0] ddr_ba,
output reg [ ROW_BITS-1:0] ddr_a,
output wire [((1<<DQ_LEVEL)+1)/2-1:0] ddr_dm,
inout [((1<<DQ_LEVEL)+1)/2-1:0] ddr_dqs,
inout [ (4<<DQ_LEVEL)-1:0] ddr_dq
);
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 [7:0] burst_len;
wire burst_last = cnt==burst_len;
reg [DQ_LEVEL-1:0] trash_lsb_addr;
reg [COL_BITS-2:0] col_addr;
wire [ROW_BITS-1:0] ddr_a_col;
generate if(COL_BITS>10) begin
assign ddr_a_col = {col_addr[COL_BITS-2:9], burst_last, col_addr[8:0], 1'b0};
end else begin
assign ddr_a_col = {burst_last, col_addr[8:0], 1'b0};
end endgenerate
wire read_accessible, read_respdone;
reg output_enable, output_enable_d1, output_enable_d2;
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 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;
// -------------------------------------------------------------------------------------
// 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
// -------------------------------------------------------------------------------------
// generate clocks
// -------------------------------------------------------------------------------------
always @ (posedge clk or negedge rstn)
if(~rstn)
{aclk,clk2} <= 2'b00;
else
{aclk,clk2} <= {aclk,clk2} + 2'b01;
// -------------------------------------------------------------------------------------
// generate user reset
// -------------------------------------------------------------------------------------
always @ (posedge aclk or negedge rstn)
if(~rstn)
aresetn <= 1'b0;
else
aresetn <= init_done;
// -------------------------------------------------------------------------------------
// refresh wptr self increasement
// -------------------------------------------------------------------------------------
always @ (posedge aclk or negedge rstn)
if(~rstn) begin
ref_cnt <= '0;
ref_idle <= 3'd1;
end else begin
if(init_done) begin
if(ref_cnt<tREFC) begin
ref_cnt <= ref_cnt + 10'd1;
end else begin
ref_cnt <= '0;
ref_idle <= ref_idle + 3'd1;
end
end
end
// -------------------------------------------------------------------------------------
// generate DDR clock
// -------------------------------------------------------------------------------------
assign ddr_ck_p = ~aclk;
assign ddr_ck_n = aclk;
assign ddr_cke = ~ddr_cs_n;
// -------------------------------------------------------------------------------------
// generate DDR DQ output behavior
// -------------------------------------------------------------------------------------
assign ddr_dm = output_enable ? '0 : 'z;
assign ddr_dqs = output_enable ? {DQS_BITS{o_dqs_c}} : 'z;
assign ddr_dq = output_enable ? o_d_d : 'z;
// -------------------------------------------------------------------------------------
// assignment for user interface (meta AXI4 interface)
// -------------------------------------------------------------------------------------
assign awready = stat==IDLE && init_done && ref_real==ref_idle;
assign wready = stat==WRITE;
assign bvalid = stat==WRESP;
assign arready = stat==IDLE && init_done && ref_real==ref_idle && ~awvalid && read_accessible;
// -------------------------------------------------------------------------------------
// main FSM for generating DDR-SDRAM behavior
// -------------------------------------------------------------------------------------
always @ (posedge aclk or negedge rstn)
if(~rstn) begin
ddr_cs_n <= 1'b1;
ddr_ras_n <= 1'b1;
ddr_cas_n <= 1'b1;
ddr_we_n <= 1'b1;
ddr_ba <= '0;
ddr_a <= DDR_A_DEFAULT;
col_addr <= '0;
burst_len <= '0;
init_done <= 1'b0;
ref_real <= 3'd0;
cnt <= 8'd0;
stat <= RESET;
end else begin
case(stat)
RESET: begin
cnt <= cnt + 8'd1;
if(cnt<8'd13) begin
end else if(cnt<8'd50) begin
ddr_cs_n <= 1'b0;
end else if(cnt<8'd51) begin
ddr_ras_n <= 1'b0;
ddr_we_n <= 1'b0;
end else if(cnt<8'd53) begin
ddr_ras_n <= 1'b1;
ddr_we_n <= 1'b1;
end else if(cnt<8'd54) begin
ddr_ras_n <= 1'b0;
ddr_cas_n <= 1'b0;
ddr_we_n <= 1'b0;
ddr_ba <= 'h1;
ddr_a <= '0;
end else begin
ddr_ba <= '0;
ddr_a <= DDR_A_MR0;
stat <= IDLE;
end
end
IDLE: begin
ddr_ras_n <= 1'b1;
ddr_cas_n <= 1'b1;
ddr_we_n <= 1'b1;
ddr_ba <= '0;
ddr_a <= DDR_A_DEFAULT;
cnt <= 8'd0;
if(ref_real != ref_idle) begin
ref_real <= ref_real + 3'd1;
stat <= REFRESH;
end else if(~init_done) begin
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;
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;
burst_len <= arlen;
stat <= RPRE;
end
end
CLEARDLL: begin
ddr_ras_n <= cnt!=8'd0;
ddr_cas_n <= cnt!=8'd0;
ddr_we_n <= cnt!=8'd0;
ddr_a <= cnt!=8'd0 ? DDR_A_DEFAULT : DDR_A_MR_CLEAR_DLL;
cnt <= cnt + 8'd1;
if(cnt==8'd255) begin
init_done <= 1'b1;
stat <= IDLE;
end
end
REFRESH: begin
cnt <= cnt + 8'd1;
if(cnt<8'd1) begin
ddr_ras_n <= 1'b0;
ddr_we_n <= 1'b0;
end else if(cnt<8'd3) begin
ddr_ras_n <= 1'b1;
ddr_we_n <= 1'b1;
end else if(cnt<8'd4) begin
ddr_ras_n <= 1'b0;
ddr_cas_n <= 1'b0;
end else if(cnt<8'd10) begin
ddr_ras_n <= 1'b1;
ddr_cas_n <= 1'b1;
end else if(cnt<8'd11) begin
ddr_ras_n <= 1'b0;
ddr_cas_n <= 1'b0;
end else if(cnt<8'd17) begin
ddr_ras_n <= 1'b1;
ddr_cas_n <= 1'b1;
end else begin
stat <= IDLE;
end
end
WPRE: begin
ddr_ras_n <= 1'b1;
cnt <= 8'd0;
stat <= WRITE;
end
WRITE: begin
ddr_a <= ddr_a_col;
if(wvalid) begin
ddr_cas_n <= 1'b0;
ddr_we_n <= 1'b0;
col_addr <= col_addr + {{(COL_BITS-2){1'b0}}, 1'b1};
if(burst_last | wlast) begin
cnt <= '0;
stat <= WRESP;
end else begin
cnt <= cnt + 8'd1;
end
end else begin
ddr_cas_n <= 1'b1;
ddr_we_n <= 1'b1;
end
end
WRESP: begin
ddr_cas_n <= 1'b1;
ddr_we_n <= 1'b1;
cnt <= cnt + 8'd1;
if(bready)
stat <= WWAIT;
end
WWAIT: begin
cnt <= cnt + 8'd1;
if(cnt>=tW2I)
stat <= IDLE;
end
RPRE: begin
ddr_ras_n <= 1'b1;
cnt <= 8'd0;
stat <= READ;
end
READ: begin
ddr_cas_n <= 1'b0;
ddr_a <= ddr_a_col;
col_addr <= col_addr + {{(COL_BITS-2){1'b0}}, 1'b1};
if(burst_last) begin
cnt <= '0;
stat <= RRESP;
end else begin
cnt <= cnt + 8'd1;
end
end
RRESP: begin
ddr_cas_n <= 1'b1;
cnt <= cnt + 8'd1;
if(read_respdone)
stat <= RWAIT;
end
RWAIT: begin
cnt <= cnt + 8'd1;
if(cnt>=tR2I)
stat <= IDLE;
end
default: stat <= IDLE;
endcase
end
// -------------------------------------------------------------------------------------
// output enable generate
// -------------------------------------------------------------------------------------
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
output_enable <= 1'b0;
output_enable_d1 <= 1'b0;
output_enable_d2 <= 1'b0;
end else begin
output_enable <= stat==WRITE || output_enable_d1 || output_enable_d2;
output_enable_d1 <= stat==WRITE;
output_enable_d2 <= output_enable_d1;
end
// -------------------------------------------------------------------------------------
// output data latches --- stage A
// -------------------------------------------------------------------------------------
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
o_v_a <= 1'b0;
{o_dh_a, o_dl_a} <= '0;
end else begin
o_v_a <= (stat==WRITE && wvalid);
{o_dh_a, o_dl_a} <= wdata;
end
// -------------------------------------------------------------------------------------
// output data latches --- stage B
// -------------------------------------------------------------------------------------
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
o_v_b <= 1'b0;
o_dh_b <= '0;
end else begin
o_v_b <= o_v_a;
o_dh_b <= o_dh_a;
end
// -------------------------------------------------------------------------------------
// dq and dqs generate for output (write)
// -------------------------------------------------------------------------------------
always @ (posedge clk2) begin
if(~aclk) begin
o_dqs_c <= 1'b0;
o_d_c <= o_v_a ? o_dl_a : '0;
end else begin
o_dqs_c <= o_v_b;
o_d_c <= o_v_b ? o_dh_b : '0;
end
end
// -------------------------------------------------------------------------------------
// dq delay for output (write)
// -------------------------------------------------------------------------------------
always @ (posedge clk)
o_d_d <= o_d_c;
// -------------------------------------------------------------------------------------
// dq sampling for input (read)
// -------------------------------------------------------------------------------------
always @ (posedge clk2) begin
i_dqs_c <= ddr_dqs;
i_d_c <= ddr_dq;
end
always @ (posedge clk2)
if(i_dqs_c)
i_d_d <= {ddr_dq, i_d_c};
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
{i_v_a, i_v_b, i_v_c, i_v_d} <= '0;
{i_l_a, i_l_b, i_l_c, i_l_d} <= '0;
end else begin
i_v_a <= stat==READ ? 1'b1 : 1'b0;
i_l_a <= burst_last;
i_v_b <= i_v_a;
i_l_b <= i_l_a & i_v_a;
i_v_c <= i_v_b;
i_l_c <= i_l_b;
i_v_d <= i_v_c;
i_l_d <= i_l_c;
end
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
i_v_e <= 1'b0;
i_l_e <= 1'b0;
i_d_e <= '0;
end else begin
i_v_e <= i_v_d;
i_l_e <= i_l_d;
i_d_e <= i_d_d;
end
// -------------------------------------------------------------------------------------
// 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} )
);
assign read_accessible = ~rvalid;
assign read_respdone = rvalid;
end else begin
assign rvalid = i_v_e;
assign rlast = i_l_e;
assign rdata = i_d_e;
assign read_accessible = 1'b1;
assign read_respdone = i_l_e;
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

BIN
UartSession.exe Normal file

Binary file not shown.

115
example-selftest/IP/pll.v Normal file
View File

@ -0,0 +1,115 @@
`timescale 1 ns / 1 ns
// synopsys translate_on
module pll (
input wire inclk0,
output wire c0,
output wire locked
);
wire [4:0] sub_wire0;
wire sub_wire2;
wire [0:0] sub_wire5 = 1'h0;
wire [0:0] sub_wire1 = sub_wire0[0:0];
assign c0 = sub_wire1;
assign locked = sub_wire2;
wire sub_wire3 = inclk0;
wire [1:0] sub_wire4 = {sub_wire5, sub_wire3};
altpll altpll_component (
.inclk (sub_wire4),
.clk (sub_wire0),
.locked (sub_wire2),
.activeclock (),
.areset (1'b0),
.clkbad (),
.clkena ({6{1'b1}}),
.clkloss (),
.clkswitch (1'b0),
.configupdate (1'b0),
.enable0 (),
.enable1 (),
.extclk (),
.extclkena ({4{1'b1}}),
.fbin (1'b1),
.fbmimicbidir (),
.fbout (),
.fref (),
.icdrclk (),
.pfdena (1'b1),
.phasecounterselect ({4{1'b1}}),
.phasedone (),
.phasestep (1'b1),
.phaseupdown (1'b1),
.pllena (1'b1),
.scanaclr (1'b0),
.scanclk (1'b0),
.scanclkena (1'b1),
.scandata (1'b0),
.scandataout (),
.scandone (),
.scanread (1'b0),
.scanwrite (1'b0),
.sclkout0 (),
.sclkout1 (),
.vcooverrange (),
.vcounderrange ());
defparam
altpll_component.bandwidth_type = "AUTO",
altpll_component.clk0_divide_by = 1,
altpll_component.clk0_duty_cycle = 50,
altpll_component.clk0_multiply_by = 6,
altpll_component.clk0_phase_shift = "0",
altpll_component.compensate_clock = "CLK0",
altpll_component.inclk0_input_frequency = 20000,
altpll_component.intended_device_family = "Cyclone IV E",
altpll_component.lpm_hint = "CBX_MODULE_PREFIX=pll",
altpll_component.lpm_type = "altpll",
altpll_component.operation_mode = "NORMAL",
altpll_component.pll_type = "AUTO",
altpll_component.port_activeclock = "PORT_UNUSED",
altpll_component.port_areset = "PORT_UNUSED",
altpll_component.port_clkbad0 = "PORT_UNUSED",
altpll_component.port_clkbad1 = "PORT_UNUSED",
altpll_component.port_clkloss = "PORT_UNUSED",
altpll_component.port_clkswitch = "PORT_UNUSED",
altpll_component.port_configupdate = "PORT_UNUSED",
altpll_component.port_fbin = "PORT_UNUSED",
altpll_component.port_inclk0 = "PORT_USED",
altpll_component.port_inclk1 = "PORT_UNUSED",
altpll_component.port_locked = "PORT_USED",
altpll_component.port_pfdena = "PORT_UNUSED",
altpll_component.port_phasecounterselect = "PORT_UNUSED",
altpll_component.port_phasedone = "PORT_UNUSED",
altpll_component.port_phasestep = "PORT_UNUSED",
altpll_component.port_phaseupdown = "PORT_UNUSED",
altpll_component.port_pllena = "PORT_UNUSED",
altpll_component.port_scanaclr = "PORT_UNUSED",
altpll_component.port_scanclk = "PORT_UNUSED",
altpll_component.port_scanclkena = "PORT_UNUSED",
altpll_component.port_scandata = "PORT_UNUSED",
altpll_component.port_scandataout = "PORT_UNUSED",
altpll_component.port_scandone = "PORT_UNUSED",
altpll_component.port_scanread = "PORT_UNUSED",
altpll_component.port_scanwrite = "PORT_UNUSED",
altpll_component.port_clk0 = "PORT_USED",
altpll_component.port_clk1 = "PORT_UNUSED",
altpll_component.port_clk2 = "PORT_UNUSED",
altpll_component.port_clk3 = "PORT_UNUSED",
altpll_component.port_clk4 = "PORT_UNUSED",
altpll_component.port_clk5 = "PORT_UNUSED",
altpll_component.port_clkena0 = "PORT_UNUSED",
altpll_component.port_clkena1 = "PORT_UNUSED",
altpll_component.port_clkena2 = "PORT_UNUSED",
altpll_component.port_clkena3 = "PORT_UNUSED",
altpll_component.port_clkena4 = "PORT_UNUSED",
altpll_component.port_clkena5 = "PORT_UNUSED",
altpll_component.port_extclk0 = "PORT_UNUSED",
altpll_component.port_extclk1 = "PORT_UNUSED",
altpll_component.port_extclk2 = "PORT_UNUSED",
altpll_component.port_extclk3 = "PORT_UNUSED",
altpll_component.self_reset_on_loss_lock = "OFF",
altpll_component.width_clock = 5;
endmodule

View File

@ -0,0 +1,113 @@
`timescale 1 ns/1 ns
module axi_self_test_master #(
parameter A_WIDTH_TEST = 26,
parameter A_WIDTH = 26,
parameter D_WIDTH = 16,
parameter D_LEVEL = 1,
parameter [7:0] WBURST_LEN = 8'd7,
parameter [7:0] RBURST_LEN = 8'd7
)(
input wire aresetn,
input wire aclk,
output wire awvalid,
input wire awready,
output reg [A_WIDTH-1:0] awaddr,
output wire [ 7:0] awlen,
output wire wvalid,
input wire wready,
output wire wlast,
output wire [D_WIDTH-1:0] wdata,
input wire bvalid,
output wire bready,
output wire arvalid,
input wire arready,
output reg [A_WIDTH-1:0] araddr,
output wire [ 7:0] arlen,
input wire rvalid,
output wire rready,
input wire rlast,
input wire [D_WIDTH-1:0] rdata,
output reg error,
output reg [ 15:0] error_cnt
);
wire aw_end;
reg awaddr_carry;
reg [7:0] w_cnt;
enum logic [2:0] {INIT, AW, W, B, AR, R} stat;
generate if(A_WIDTH_TEST<A_WIDTH)
assign aw_end = awaddr[A_WIDTH_TEST];
else
assign aw_end = awaddr_carry;
endgenerate
assign awvalid = stat==AW;
assign awlen = WBURST_LEN;
assign wvalid = stat==W;
assign wlast = w_cnt==WBURST_LEN;
assign wdata = awaddr;
assign bready = 1'b1;
assign arvalid = stat==AR;
assign arlen = RBURST_LEN;
assign rready = 1'b1;
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
{awaddr_carry, awaddr} <= '0;
w_cnt <= 8'd0;
araddr <= '0;
stat <= INIT;
end else begin
case(stat)
INIT: begin
{awaddr_carry, awaddr} <= '0;
w_cnt <= 8'd0;
araddr <= '0;
stat <= AW;
end
AW: if(awready) begin
w_cnt <= 8'd0;
stat <= W;
end
W: if(wready) begin
{awaddr_carry, awaddr} <= {awaddr_carry, awaddr} + (1<<D_LEVEL);
w_cnt <= w_cnt + 8'd1;
if(wlast)
stat <= B;
end
B: if(bvalid) begin
stat <= aw_end ? AR : AW;
end
AR: if(arready) begin
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;
if(araddr_next[A_WIDTH_TEST])
araddr <= '0;
end
end
endcase
end
// ------------------------------------------------------------
// read and write mismatch detect
// ------------------------------------------------------------
wire [D_WIDTH-1:0] rdata_idle = araddr;
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
error <= 1'b0;
error_cnt <= 16'd0;
end else begin
error <= rvalid && rready && rdata!=rdata_idle;
if(error)
error_cnt <= error_cnt + 16'd1;
end
endmodule

158
example-selftest/RTL/top.sv Normal file
View File

@ -0,0 +1,158 @@
`timescale 1 ns/1 ns
module top(
input wire clk50m,
output wire ddr_ck_p, ddr_ck_n,
output wire ddr_cke,
output wire ddr_cs_n, ddr_ras_n, ddr_cas_n, ddr_we_n,
output wire [ 1:0] ddr_ba,
output wire [12:0] ddr_a,
inout [ 7:0] ddr_dq,
inout [ 0:0] ddr_dqs,
output wire [ 0:0] ddr_dm,
output wire error,
output wire [15:0] error_cnt
);
// -------------------------------------------------------------------------------------
// DDR-SDRAM parameters
// -------------------------------------------------------------------------------------
localparam BA_BITS = 2;
localparam ROW_BITS = 13;
localparam COL_BITS = 11;
localparam DQ_LEVEL = 1;
localparam DQ_BITS = (4<<DQ_LEVEL);
localparam DQS_BITS = ((1<<DQ_LEVEL)+1)/2;
// -------------------------------------------------------------------------------------
// meta AXI4 parameters
// -------------------------------------------------------------------------------------
localparam A_WIDTH = BA_BITS+ROW_BITS+COL_BITS+DQ_LEVEL-1;
localparam D_WIDTH = (8<<DQ_LEVEL);
// -------------------------------------------------------------------------------------
// driving clock and reset
// -------------------------------------------------------------------------------------
wire clk300m;
wire rstn;
// -------------------------------------------------------------------------------------
// meta AXI4 interface
// -------------------------------------------------------------------------------------
wire aresetn;
wire aclk;
wire awvalid;
wire awready;
wire [A_WIDTH-1:0] awaddr;
wire [ 7:0] awlen;
wire wvalid;
wire wready;
wire wlast;
wire [D_WIDTH-1:0] wdata;
wire bvalid;
wire bready;
wire arvalid;
wire arready;
wire [A_WIDTH-1:0] araddr;
wire [ 7:0] arlen;
wire rvalid;
wire rready;
wire rlast;
wire [D_WIDTH-1:0] rdata;
// -------------------------------------------------------------------------------------
// PLL for generating 300MHz clock
// -------------------------------------------------------------------------------------
pll pll_i(
.inclk0 ( clk50m ),
.c0 ( clk300m ),
.locked ( rstn )
);
// -------------------------------------------------------------------------------------
// meta AXI4 master for testing
// -------------------------------------------------------------------------------------
axi_self_test_master #(
.A_WIDTH ( A_WIDTH ),
.A_WIDTH_TEST( A_WIDTH ),
.D_WIDTH ( D_WIDTH ),
.D_LEVEL ( DQ_LEVEL ),
.WBURST_LEN ( 8'd15 ),
.RBURST_LEN ( 8'd15 )
) axi_m_i (
.aresetn ( aresetn ),
.aclk ( aclk ),
.awvalid ( awvalid ),
.awready ( awready ),
.awaddr ( awaddr ),
.awlen ( awlen ),
.wvalid ( wvalid ),
.wready ( wready ),
.wlast ( wlast ),
.wdata ( wdata ),
.bvalid ( bvalid ),
.bready ( bready ),
.arvalid ( arvalid ),
.arready ( arready ),
.araddr ( araddr ),
.arlen ( arlen ),
.rvalid ( rvalid ),
.rready ( rready ),
.rlast ( rlast ),
.rdata ( rdata ),
.error ( error ),
.error_cnt ( error_cnt )
);
// -------------------------------------------------------------------------------------
// DDR-SDRAM controller
// -------------------------------------------------------------------------------------
ddr_sdram_ctrl #(
.READ_BUFFER ( 1 ),
.BA_BITS ( BA_BITS ),
.ROW_BITS ( ROW_BITS ),
.COL_BITS ( COL_BITS ),
.DQ_LEVEL ( DQ_LEVEL ), // x8
.tREFC ( 10'd512 ),
.tW2I ( 8'd7 ),
.tR2I ( 8'd7 )
) ddr_ctrl_i(
.rstn ( rstn ),
.clk ( clk300m ),
.aresetn ( aresetn ),
.aclk ( aclk ),
.awvalid ( awvalid ),
.awready ( awready ),
.awaddr ( awaddr ),
.awlen ( awlen ),
.wvalid ( wvalid ),
.wready ( wready ),
.wlast ( wlast ),
.wdata ( wdata ),
.bvalid ( bvalid ),
.bready ( bready ),
.arvalid ( arvalid ),
.arready ( arready ),
.araddr ( araddr ),
.arlen ( arlen ),
.rvalid ( rvalid ),
.rready ( rready ),
.rlast ( rlast ),
.rdata ( rdata ),
.ddr_ck_p ( ddr_ck_p ),
.ddr_ck_n ( ddr_ck_n ),
.ddr_cke ( ddr_cke ),
.ddr_cs_n ( ddr_cs_n ),
.ddr_ras_n ( ddr_ras_n ),
.ddr_cas_n ( ddr_cas_n ),
.ddr_we_n ( ddr_we_n ),
.ddr_ba ( ddr_ba ),
.ddr_a ( ddr_a ),
.ddr_dq ( ddr_dq ),
.ddr_dqs ( ddr_dqs ),
.ddr_dm ( ddr_dm )
);
endmodule

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,30 @@
# -------------------------------------------------------------------------- #
#
# Copyright (C) 1991-2013 Altera Corporation
# Your use of Altera Corporation's design tools, logic functions
# and other software and tools, and its AMPP partner logic
# functions, and any output files from any of the foregoing
# (including device programming or simulation files), and any
# associated documentation or information are expressly subject
# to the terms and conditions of the Altera Program License
# Subscription Agreement, Altera MegaCore Function License
# Agreement, or other applicable license agreement, including,
# without limitation, that your use is for the sole purpose of
# programming logic devices manufactured by Altera and sold by
# Altera or its authorized distributors. Please refer to the
# applicable agreement for further details.
#
# -------------------------------------------------------------------------- #
#
# Quartus II 64-Bit
# Version 13.1.0 Build 162 10/23/2013 SJ Full Version
# Date created = 21:02:52 January 17, 2021
#
# -------------------------------------------------------------------------- #
QUARTUS_VERSION = "13.1"
DATE = "21:02:52 January 17, 2021"
# Revisions
PROJECT_REVISION = "ddr_test"

View File

@ -0,0 +1,179 @@
# -------------------------------------------------------------------------- #
#
# Copyright (C) 1991-2013 Altera Corporation
# Your use of Altera Corporation's design tools, logic functions
# and other software and tools, and its AMPP partner logic
# functions, and any output files from any of the foregoing
# (including device programming or simulation files), and any
# associated documentation or information are expressly subject
# to the terms and conditions of the Altera Program License
# Subscription Agreement, Altera MegaCore Function License
# Agreement, or other applicable license agreement, including,
# without limitation, that your use is for the sole purpose of
# programming logic devices manufactured by Altera and sold by
# Altera or its authorized distributors. Please refer to the
# applicable agreement for further details.
#
# -------------------------------------------------------------------------- #
#
# Quartus II 64-Bit
# Version 13.1.0 Build 162 10/23/2013 SJ Full Version
# Date created = 21:02:52 January 17, 2021
#
# -------------------------------------------------------------------------- #
#
# Notes:
#
# 1) The default values for assignments are stored in the file:
# ddr_test_assignment_defaults.qdf
# If this file doesn't exist, see file:
# assignment_defaults.qdf
#
# 2) Altera recommends that you do not modify this file. This
# file is updated automatically by the Quartus II software
# and any changes you make may be lost or overwritten.
#
# -------------------------------------------------------------------------- #
set_global_assignment -name FAMILY "Cyclone IV E"
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 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_location_assignment PIN_23 -to clk50m
set_location_assignment PIN_10 -to ddr_cs_n
set_location_assignment PIN_30 -to ddr_ras_n
set_location_assignment PIN_31 -to ddr_cas_n
set_location_assignment PIN_32 -to ddr_we_n
set_location_assignment PIN_86 -to ddr_cke
set_location_assignment PIN_85 -to ddr_ck_p
set_location_assignment PIN_84 -to ddr_ck_n
set_location_assignment PIN_7 -to ddr_ba[0]
set_location_assignment PIN_3 -to ddr_ba[1]
set_location_assignment PIN_1 -to ddr_a[0]
set_location_assignment PIN_144 -to ddr_a[1]
set_location_assignment PIN_143 -to ddr_a[2]
set_location_assignment PIN_142 -to ddr_a[3]
set_location_assignment PIN_106 -to ddr_a[4]
set_location_assignment PIN_105 -to ddr_a[5]
set_location_assignment PIN_104 -to ddr_a[6]
set_location_assignment PIN_103 -to ddr_a[7]
set_location_assignment PIN_100 -to ddr_a[8]
set_location_assignment PIN_99 -to ddr_a[9]
set_location_assignment PIN_2 -to ddr_a[10]
set_location_assignment PIN_98 -to ddr_a[11]
set_location_assignment PIN_87 -to ddr_a[12]
set_location_assignment PIN_83 -to ddr_dm[0]
set_location_assignment PIN_80 -to ddr_dqs[0]
set_location_assignment PIN_38 -to ddr_dq[0]
set_location_assignment PIN_39 -to ddr_dq[1]
set_location_assignment PIN_34 -to ddr_dq[2]
set_location_assignment PIN_33 -to ddr_dq[3]
set_location_assignment PIN_76 -to ddr_dq[4]
set_location_assignment PIN_75 -to ddr_dq[5]
set_location_assignment PIN_74 -to ddr_dq[6]
set_location_assignment PIN_73 -to ddr_dq[7]
set_location_assignment PIN_42 -to error
set_location_assignment PIN_141 -to error_cnt[0]
set_location_assignment PIN_138 -to error_cnt[1]
set_location_assignment PIN_137 -to error_cnt[2]
set_location_assignment PIN_136 -to error_cnt[3]
set_location_assignment PIN_135 -to error_cnt[4]
set_location_assignment PIN_133 -to error_cnt[5]
set_location_assignment PIN_132 -to error_cnt[6]
set_location_assignment PIN_129 -to error_cnt[7]
set_location_assignment PIN_128 -to error_cnt[8]
set_location_assignment PIN_127 -to error_cnt[9]
set_location_assignment PIN_126 -to error_cnt[10]
set_location_assignment PIN_125 -to error_cnt[11]
set_location_assignment PIN_124 -to error_cnt[12]
set_location_assignment PIN_121 -to error_cnt[13]
set_location_assignment PIN_120 -to error_cnt[14]
set_location_assignment PIN_119 -to error_cnt[15]
set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
set_global_assignment -name ENABLE_SIGNALTAP ON
set_global_assignment -name USE_SIGNALTAP_FILE SignalTap/stp1.stp
set_global_assignment -name SLD_NODE_CREATOR_ID 110 -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_ENTITY_NAME sld_signaltap -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_RAM_BLOCK_TYPE=AUTO" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_TRIGGER_BITS=1" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_NODE_INFO=805334528" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_POWER_UP_TRIGGER=0" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_INVERSION_MASK=00000000000000000000000000" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_INVERSION_MASK_LENGTH=26" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_STORAGE_QUALIFIER_INVERSION_MASK_LENGTH=0" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_SEGMENT_SIZE=512" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_ATTRIBUTE_MEM_MODE=OFF" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_STATE_FLOW_USE_GENERATED=0" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_STATE_BITS=11" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_BUFFER_FULL_STOP=1" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_CURRENT_RESOURCE_WIDTH=1" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_TRIGGER_LEVEL=1" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_SAMPLE_DEPTH=512" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_TRIGGER_IN_ENABLED=0" -section_id auto_signaltap_0
set_global_assignment -name SLD_NODE_PARAMETER_ASSIGNMENT "SLD_ADVANCED_TRIGGER_ENTITY=basic,1," -section_id auto_signaltap_0
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 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
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[2] -to ddr_a[11] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[3] -to ddr_a[12] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[4] -to ddr_a[1] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[5] -to ddr_a[2] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[6] -to ddr_a[3] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[7] -to ddr_a[4] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[8] -to ddr_a[5] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[9] -to ddr_a[6] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[10] -to ddr_a[7] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[11] -to ddr_a[8] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[12] -to ddr_a[9] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[13] -to ddr_ba[0] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[14] -to ddr_ba[1] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[15] -to ddr_cas_n -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[16] -to ddr_ck_p -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[17] -to ddr_cs_n -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[18] -to ddr_ras_n -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[19] -to ddr_we_n -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[20] -to error -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[21] -to error_cnt[0] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[22] -to error_cnt[10] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[23] -to error_cnt[11] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[24] -to error_cnt[12] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[25] -to error_cnt[13] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[26] -to error_cnt[14] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[27] -to error_cnt[15] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[28] -to error_cnt[1] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[29] -to error_cnt[2] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[30] -to error_cnt[3] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[31] -to error_cnt[4] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[32] -to error_cnt[5] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[33] -to error_cnt[6] -section_id auto_signaltap_0
set_instance_assignment -name CONNECT_TO_SLD_NODE_ENTITY_PORT acq_data_in[34] -to error_cnt[7] -section_id auto_signaltap_0
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_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top
set_global_assignment -name SLD_FILE db/stp1_auto_stripped.stp

View File

@ -0,0 +1,115 @@
`timescale 1 ns / 1 ns
// synopsys translate_on
module pll (
input wire inclk0,
output wire c0,
output wire locked
);
wire [4:0] sub_wire0;
wire sub_wire2;
wire [0:0] sub_wire5 = 1'h0;
wire [0:0] sub_wire1 = sub_wire0[0:0];
assign c0 = sub_wire1;
assign locked = sub_wire2;
wire sub_wire3 = inclk0;
wire [1:0] sub_wire4 = {sub_wire5, sub_wire3};
altpll altpll_component (
.inclk (sub_wire4),
.clk (sub_wire0),
.locked (sub_wire2),
.activeclock (),
.areset (1'b0),
.clkbad (),
.clkena ({6{1'b1}}),
.clkloss (),
.clkswitch (1'b0),
.configupdate (1'b0),
.enable0 (),
.enable1 (),
.extclk (),
.extclkena ({4{1'b1}}),
.fbin (1'b1),
.fbmimicbidir (),
.fbout (),
.fref (),
.icdrclk (),
.pfdena (1'b1),
.phasecounterselect ({4{1'b1}}),
.phasedone (),
.phasestep (1'b1),
.phaseupdown (1'b1),
.pllena (1'b1),
.scanaclr (1'b0),
.scanclk (1'b0),
.scanclkena (1'b1),
.scandata (1'b0),
.scandataout (),
.scandone (),
.scanread (1'b0),
.scanwrite (1'b0),
.sclkout0 (),
.sclkout1 (),
.vcooverrange (),
.vcounderrange ());
defparam
altpll_component.bandwidth_type = "AUTO",
altpll_component.clk0_divide_by = 1,
altpll_component.clk0_duty_cycle = 50,
altpll_component.clk0_multiply_by = 6,
altpll_component.clk0_phase_shift = "0",
altpll_component.compensate_clock = "CLK0",
altpll_component.inclk0_input_frequency = 20000,
altpll_component.intended_device_family = "Cyclone IV E",
altpll_component.lpm_hint = "CBX_MODULE_PREFIX=pll",
altpll_component.lpm_type = "altpll",
altpll_component.operation_mode = "NORMAL",
altpll_component.pll_type = "AUTO",
altpll_component.port_activeclock = "PORT_UNUSED",
altpll_component.port_areset = "PORT_UNUSED",
altpll_component.port_clkbad0 = "PORT_UNUSED",
altpll_component.port_clkbad1 = "PORT_UNUSED",
altpll_component.port_clkloss = "PORT_UNUSED",
altpll_component.port_clkswitch = "PORT_UNUSED",
altpll_component.port_configupdate = "PORT_UNUSED",
altpll_component.port_fbin = "PORT_UNUSED",
altpll_component.port_inclk0 = "PORT_USED",
altpll_component.port_inclk1 = "PORT_UNUSED",
altpll_component.port_locked = "PORT_USED",
altpll_component.port_pfdena = "PORT_UNUSED",
altpll_component.port_phasecounterselect = "PORT_UNUSED",
altpll_component.port_phasedone = "PORT_UNUSED",
altpll_component.port_phasestep = "PORT_UNUSED",
altpll_component.port_phaseupdown = "PORT_UNUSED",
altpll_component.port_pllena = "PORT_UNUSED",
altpll_component.port_scanaclr = "PORT_UNUSED",
altpll_component.port_scanclk = "PORT_UNUSED",
altpll_component.port_scanclkena = "PORT_UNUSED",
altpll_component.port_scandata = "PORT_UNUSED",
altpll_component.port_scandataout = "PORT_UNUSED",
altpll_component.port_scandone = "PORT_UNUSED",
altpll_component.port_scanread = "PORT_UNUSED",
altpll_component.port_scanwrite = "PORT_UNUSED",
altpll_component.port_clk0 = "PORT_USED",
altpll_component.port_clk1 = "PORT_UNUSED",
altpll_component.port_clk2 = "PORT_UNUSED",
altpll_component.port_clk3 = "PORT_UNUSED",
altpll_component.port_clk4 = "PORT_UNUSED",
altpll_component.port_clk5 = "PORT_UNUSED",
altpll_component.port_clkena0 = "PORT_UNUSED",
altpll_component.port_clkena1 = "PORT_UNUSED",
altpll_component.port_clkena2 = "PORT_UNUSED",
altpll_component.port_clkena3 = "PORT_UNUSED",
altpll_component.port_clkena4 = "PORT_UNUSED",
altpll_component.port_clkena5 = "PORT_UNUSED",
altpll_component.port_extclk0 = "PORT_UNUSED",
altpll_component.port_extclk1 = "PORT_UNUSED",
altpll_component.port_extclk2 = "PORT_UNUSED",
altpll_component.port_extclk3 = "PORT_UNUSED",
altpll_component.self_reset_on_loss_lock = "OFF",
altpll_component.width_clock = 5;
endmodule

View File

@ -0,0 +1,138 @@
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

@ -0,0 +1,153 @@
`timescale 1 ns/1 ns
module top(
input wire clk50m,
output wire uart_tx,
input wire uart_rx,
output wire ddr_ck_p, ddr_ck_n,
output wire ddr_cke,
output wire ddr_cs_n, ddr_ras_n, ddr_cas_n, ddr_we_n,
output wire [ 1:0] ddr_ba,
output wire [12:0] ddr_a,
output wire [ 0:0] ddr_dm,
inout [ 0:0] ddr_dqs,
inout [ 7:0] ddr_dq
);
// -------------------------------------------------------------------------------------
// DDR-SDRAM parameters
// -------------------------------------------------------------------------------------
localparam BA_BITS = 2;
localparam ROW_BITS = 13;
localparam COL_BITS = 11;
localparam DQ_LEVEL = 1;
localparam DQ_BITS = (4<<DQ_LEVEL);
localparam DQS_BITS = ((1<<DQ_LEVEL)+1)/2;
// -------------------------------------------------------------------------------------
// meta AXI4 parameters
// -------------------------------------------------------------------------------------
localparam A_WIDTH = BA_BITS+ROW_BITS+COL_BITS+DQ_LEVEL-1;
localparam D_WIDTH = (8<<DQ_LEVEL);
// -------------------------------------------------------------------------------------
// driving clock and reset
// -------------------------------------------------------------------------------------
wire clk300m;
wire rstn;
// -------------------------------------------------------------------------------------
// meta AXI4 interface
// -------------------------------------------------------------------------------------
wire aresetn;
wire aclk;
wire awvalid;
wire awready;
wire [A_WIDTH-1:0] awaddr;
wire [ 7:0] awlen;
wire wvalid;
wire wready;
wire wlast;
wire [D_WIDTH-1:0] wdata;
wire bvalid;
wire bready;
wire arvalid;
wire arready;
wire [A_WIDTH-1:0] araddr;
wire [ 7:0] arlen;
wire rvalid;
wire rready;
wire rlast;
wire [D_WIDTH-1:0] rdata;
// -------------------------------------------------------------------------------------
// PLL for generating 300MHz clock
// -------------------------------------------------------------------------------------
pll pll_i(
.inclk0 ( clk50m ),
.c0 ( clk300m ),
.locked ( rstn )
);
// -------------------------------------------------------------------------------------
// meta AXI4 master for testing
// -------------------------------------------------------------------------------------
uart2axi4 #(
.A_WIDTH ( A_WIDTH )
) uart_axi_i (
.aresetn ( aresetn ),
.aclk ( aclk ),
.awvalid ( awvalid ),
.awready ( awready ),
.awaddr ( awaddr ),
.awlen ( awlen ),
.wvalid ( wvalid ),
.wready ( wready ),
.wlast ( wlast ),
.wdata ( wdata ),
.bvalid ( bvalid ),
.bready ( bready ),
.arvalid ( arvalid ),
.arready ( arready ),
.araddr ( araddr ),
.arlen ( arlen ),
.rvalid ( rvalid ),
.rready ( rready ),
.rlast ( rlast ),
.rdata ( rdata ),
.uart_tx ( uart_tx ),
.uart_rx ( uart_rx )
);
// -------------------------------------------------------------------------------------
// DDR-SDRAM controller
// -------------------------------------------------------------------------------------
ddr_sdram_ctrl #(
.READ_BUFFER ( 1 ),
.BA_BITS ( BA_BITS ),
.ROW_BITS ( ROW_BITS ),
.COL_BITS ( COL_BITS ),
.DQ_LEVEL ( DQ_LEVEL ), // x8
.tREFC ( 10'd512 ),
.tW2I ( 8'd7 ),
.tR2I ( 8'd7 )
) ddr_ctrl_i(
.rstn ( rstn ),
.clk ( clk300m ),
.aresetn ( aresetn ),
.aclk ( aclk ),
.awvalid ( awvalid ),
.awready ( awready ),
.awaddr ( awaddr ),
.awlen ( awlen ),
.wvalid ( wvalid ),
.wready ( wready ),
.wlast ( wlast ),
.wdata ( wdata ),
.bvalid ( bvalid ),
.bready ( bready ),
.arvalid ( arvalid ),
.arready ( arready ),
.araddr ( araddr ),
.arlen ( arlen ),
.rvalid ( rvalid ),
.rready ( rready ),
.rlast ( rlast ),
.rdata ( rdata ),
.ddr_ck_p ( ddr_ck_p ),
.ddr_ck_n ( ddr_ck_n ),
.ddr_cke ( ddr_cke ),
.ddr_cs_n ( ddr_cs_n ),
.ddr_ras_n ( ddr_ras_n ),
.ddr_cas_n ( ddr_cas_n ),
.ddr_we_n ( ddr_we_n ),
.ddr_ba ( ddr_ba ),
.ddr_a ( ddr_a ),
.ddr_dq ( ddr_dq ),
.ddr_dqs ( ddr_dqs ),
.ddr_dm ( ddr_dm )
);
endmodule

View File

@ -0,0 +1,265 @@
`timescale 1 ns/1 ns
module uart2axi4 #(
parameter A_WIDTH = 26,
parameter D_WIDTH = 16
) (
input wire aresetn,
input wire aclk,
output wire awvalid,
input wire awready,
output reg [A_WIDTH-1:0] awaddr,
output reg [ 7:0] awlen,
output wire wvalid,
input wire wready,
output wire wlast,
output wire [D_WIDTH-1:0] wdata,
input wire bvalid,
output wire bready,
output wire arvalid,
input wire arready,
output reg [A_WIDTH-1:0] araddr,
output reg [ 7:0] arlen,
input wire rvalid,
output wire rready,
input wire rlast,
input wire [D_WIDTH-1:0] rdata,
input wire uart_rx,
output wire uart_tx
);
function automatic logic isW(input [7:0] c);
return c==8'h57 || c==8'h77;
endfunction
function automatic logic isR(input [7:0] c);
return c==8'h52 || c==8'h72;
endfunction
function automatic logic isSpace(input [7:0] c);
return c==8'h20 || c==8'h09;
endfunction
function automatic logic isNewline(input [7:0] c);
return c==8'h0D || c==8'h0A;
endfunction
function automatic logic [3:0] isHex(input [7:0] c);
if(c>=8'h30 && c<= 8'h39)
return 1'b1;
else if(c>=8'h41 && c<=8'h46 || c>=8'h61 && c<=8'h66)
return 1'b1;
else
return 1'b0;
endfunction
function automatic logic [3:0] getHex(input [7:0] c);
if(c>=8'h30 && c<= 8'h39)
return c[3:0];
else if(c>=8'h41 && c<=8'h46 || c>=8'h61 && c<=8'h66)
return c[3:0] + 8'h9;
else
return 4'd0;
endfunction
localparam V_WIDTH = A_WIDTH>D_WIDTH ? (A_WIDTH>8?A_WIDTH:8) : (D_WIDTH>8?D_WIDTH:8);
wire rx_valid;
wire [7:0] rx_data;
reg rw; // 0:write 1:read
reg [V_WIDTH-1:0] value;
reg [ 7:0] value_cnt;
reg wbuf_wen;
reg [ 7:0] wbuf_waddr;
reg [D_WIDTH-1:0] wbuf_wdata;
reg [ 7:0] wbuf_raddr;
wire[ 7:0] wbuf_raddr_n = stat == AXI_W && wready ? wbuf_raddr + 8'd1 : wbuf_raddr;
wire[D_WIDTH-1:0] wbuf_rdata;
enum logic [3:0] {IDLE, INVALID, GADDR, GRLEN, GWDATA, AXI_AR, AXI_R, AXI_AW, AXI_W, AXI_B} stat;
assign awvalid = stat == AXI_AW;
assign wvalid = stat == AXI_W;
assign wlast = wbuf_raddr == wbuf_waddr;
assign wdata = wbuf_rdata;
assign bready = stat == AXI_B;
assign arvalid = stat == AXI_AR;
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
awaddr <= '0;
awlen <= '0;
araddr <= '0;
arlen <= '0;
rw <= 1'b0;
value <= '0;
value_cnt <= '0;
wbuf_wen <= 1'b0;
wbuf_waddr <= '0;
wbuf_wdata <= '0;
wbuf_raddr <= '0;
stat <= IDLE;
end else begin
wbuf_wen <= 1'b0;
case(stat)
IDLE: if(rx_valid) begin
value <= '0;
value_cnt <= '0;
wbuf_raddr <= '0;
if( isW(rx_data) ) begin
rw <= 1'b0;
stat <= GADDR;
end else if( isR(rx_data) ) begin
rw <= 1'b1;
stat <= GADDR;
end else if( ~isNewline(rx_data) ) begin
stat <= INVALID;
end
end
GADDR: if(rx_valid) begin
if( isNewline(rx_data) ) begin
value <= '0;
stat <= IDLE;
end else if( isSpace(rx_data) ) begin
value <= '0;
if(rw) begin
araddr <= value[A_WIDTH-1:0];
stat <= GRLEN;
end else begin
awaddr <= value[A_WIDTH-1:0];
stat <= GWDATA;
end
end else if( isHex(rx_data) ) begin
value <= { value[V_WIDTH-5:0], getHex(rx_data) };
end else begin
stat <= INVALID;
end
end
GRLEN: if(rx_valid) begin
if( isNewline(rx_data) ) begin
value <= '0;
arlen <= value[7:0];
stat <= AXI_AR;
end else if( isHex(rx_data) ) begin
value <= { value[V_WIDTH-5:0], getHex(rx_data) };
end else begin
stat <= INVALID;
end
end
GWDATA: if(rx_valid) begin
if( isNewline(rx_data) ) begin
wbuf_wen <= 1'b1;
wbuf_waddr <= value_cnt;
wbuf_wdata <= value[D_WIDTH-1:0];
awlen <= value_cnt;
stat <= AXI_AW;
end else if( isSpace(rx_data) ) begin
value <= '0;
value_cnt <= value_cnt + 8'd1;
wbuf_wen <= 1'b1;
wbuf_waddr <= value_cnt;
wbuf_wdata <= value[D_WIDTH-1:0];
end else if( isHex(rx_data) ) begin
value <= { value[V_WIDTH-5:0], getHex(rx_data) };
end else begin
stat <= INVALID;
end
end
INVALID: if( rx_valid ) begin
if ( isNewline(rx_data) )
stat <= IDLE;
end
AXI_AR: if(arready) begin
stat <= AXI_R;
end
AXI_R: if(rvalid & rready & rlast) begin
stat <= IDLE;
end
AXI_AW: if(awready) begin
stat <= AXI_W;
end
AXI_W: if(wready) begin
wbuf_raddr <= wbuf_raddr + 8'd1;
if(wbuf_raddr==awlen)
stat <= AXI_B;
end
AXI_B: if(bvalid) begin
stat <= IDLE;
end
default: stat<=IDLE;
endcase
end
ram_for_axi4write #(
.ADDR_LEN ( 8 ),
.DATA_LEN ( D_WIDTH )
) ram_for_axi4write_i (
.clk ( aclk ),
.wr_req ( wbuf_wen ),
.wr_addr ( wbuf_waddr ),
.wr_data ( wbuf_wdata ),
.rd_addr ( wbuf_raddr_n ),
.rd_data ( wbuf_rdata )
);
uart_rx#(
.CLK_DIV ( 162 ),
.CLK_PART ( 6 )
) uart_rx_i (
.rstn ( aresetn ),
.clk ( aclk ),
.rx ( uart_rx ),
.rvalid ( rx_valid ),
.rdata ( rx_data )
);
axis2uarttx #(
.CLK_DIV ( 651 ),
.DATA_WIDTH ( D_WIDTH ),
.FIFO_ASIZE ( 10 )
) uart_tx_i (
.aresetn ( aresetn ),
.aclk ( aclk ),
.tvalid ( rvalid ),
.tready ( rready ),
.tlast ( rlast ),
.tdata ( rdata ),
.uart_tx ( uart_tx )
);
endmodule
module ram_for_axi4write #(
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

@ -0,0 +1,71 @@
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

@ -0,0 +1,30 @@
# -------------------------------------------------------------------------- #
#
# Copyright (C) 1991-2013 Altera Corporation
# Your use of Altera Corporation's design tools, logic functions
# and other software and tools, and its AMPP partner logic
# functions, and any output files from any of the foregoing
# (including device programming or simulation files), and any
# associated documentation or information are expressly subject
# to the terms and conditions of the Altera Program License
# Subscription Agreement, Altera MegaCore Function License
# Agreement, or other applicable license agreement, including,
# without limitation, that your use is for the sole purpose of
# programming logic devices manufactured by Altera and sold by
# Altera or its authorized distributors. Please refer to the
# applicable agreement for further details.
#
# -------------------------------------------------------------------------- #
#
# Quartus II 64-Bit
# Version 13.1.0 Build 162 10/23/2013 SJ Full Version
# Date created = 17:45:24 January 23, 2021
#
# -------------------------------------------------------------------------- #
QUARTUS_VERSION = "13.1"
DATE = "17:45:24 January 23, 2021"
# Revisions
PROJECT_REVISION = "ddr_test"

View File

@ -0,0 +1,100 @@
# -------------------------------------------------------------------------- #
#
# Copyright (C) 1991-2013 Altera Corporation
# Your use of Altera Corporation's design tools, logic functions
# and other software and tools, and its AMPP partner logic
# functions, and any output files from any of the foregoing
# (including device programming or simulation files), and any
# associated documentation or information are expressly subject
# to the terms and conditions of the Altera Program License
# Subscription Agreement, Altera MegaCore Function License
# Agreement, or other applicable license agreement, including,
# without limitation, that your use is for the sole purpose of
# programming logic devices manufactured by Altera and sold by
# Altera or its authorized distributors. Please refer to the
# applicable agreement for further details.
#
# -------------------------------------------------------------------------- #
#
# Quartus II 64-Bit
# Version 13.1.0 Build 162 10/23/2013 SJ Full Version
# Date created = 17:45:24 January 23, 2021
#
# -------------------------------------------------------------------------- #
#
# Notes:
#
# 1) The default values for assignments are stored in the file:
# ddr_test_assignment_defaults.qdf
# If this file doesn't exist, see file:
# assignment_defaults.qdf
#
# 2) Altera recommends that you do not modify this file. This
# file is updated automatically by the Quartus II software
# and any changes you make may be lost or overwritten.
#
# -------------------------------------------------------------------------- #
set_global_assignment -name FAMILY "Cyclone IV E"
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 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_location_assignment PIN_23 -to clk50m
set_location_assignment PIN_28 -to uart_tx
set_location_assignment PIN_25 -to uart_rx
set_location_assignment PIN_10 -to ddr_cs_n
set_location_assignment PIN_30 -to ddr_ras_n
set_location_assignment PIN_31 -to ddr_cas_n
set_location_assignment PIN_32 -to ddr_we_n
set_location_assignment PIN_86 -to ddr_cke
set_location_assignment PIN_85 -to ddr_ck_p
set_location_assignment PIN_84 -to ddr_ck_n
set_location_assignment PIN_7 -to ddr_ba[0]
set_location_assignment PIN_3 -to ddr_ba[1]
set_location_assignment PIN_1 -to ddr_a[0]
set_location_assignment PIN_144 -to ddr_a[1]
set_location_assignment PIN_143 -to ddr_a[2]
set_location_assignment PIN_142 -to ddr_a[3]
set_location_assignment PIN_106 -to ddr_a[4]
set_location_assignment PIN_105 -to ddr_a[5]
set_location_assignment PIN_104 -to ddr_a[6]
set_location_assignment PIN_103 -to ddr_a[7]
set_location_assignment PIN_100 -to ddr_a[8]
set_location_assignment PIN_99 -to ddr_a[9]
set_location_assignment PIN_2 -to ddr_a[10]
set_location_assignment PIN_98 -to ddr_a[11]
set_location_assignment PIN_87 -to ddr_a[12]
set_location_assignment PIN_83 -to ddr_dm[0]
set_location_assignment PIN_80 -to ddr_dqs[0]
set_location_assignment PIN_38 -to ddr_dq[0]
set_location_assignment PIN_39 -to ddr_dq[1]
set_location_assignment PIN_34 -to ddr_dq[2]
set_location_assignment PIN_33 -to ddr_dq[3]
set_location_assignment PIN_76 -to ddr_dq[4]
set_location_assignment PIN_75 -to ddr_dq[5]
set_location_assignment PIN_74 -to ddr_dq[6]
set_location_assignment PIN_73 -to ddr_dq[7]
set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top

BIN
hardware/gerber.zip Normal file

Binary file not shown.

19146
hardware/sch.pdf Normal file

File diff suppressed because it is too large Load Diff

BIN
images/UartSession.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
images/board.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
images/sim.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

1436
sim-selftest/DDR_MODEL/ddr.v Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,124 @@
/****************************************************************************************
*
* 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

View File

@ -0,0 +1,113 @@
`timescale 1 ns/1 ns
module axi_self_test_master #(
parameter A_WIDTH_TEST = 26,
parameter A_WIDTH = 26,
parameter D_WIDTH = 16,
parameter D_LEVEL = 1,
parameter [7:0] WBURST_LEN = 8'd7,
parameter [7:0] RBURST_LEN = 8'd7
)(
input wire aresetn,
input wire aclk,
output wire awvalid,
input wire awready,
output reg [A_WIDTH-1:0] awaddr,
output wire [ 7:0] awlen,
output wire wvalid,
input wire wready,
output wire wlast,
output wire [D_WIDTH-1:0] wdata,
input wire bvalid,
output wire bready,
output wire arvalid,
input wire arready,
output reg [A_WIDTH-1:0] araddr,
output wire [ 7:0] arlen,
input wire rvalid,
output wire rready,
input wire rlast,
input wire [D_WIDTH-1:0] rdata,
output reg error,
output reg [ 15:0] error_cnt
);
wire aw_end;
reg awaddr_carry;
reg [7:0] w_cnt;
enum logic [2:0] {INIT, AW, W, B, AR, R} stat;
generate if(A_WIDTH_TEST<A_WIDTH)
assign aw_end = awaddr[A_WIDTH_TEST];
else
assign aw_end = awaddr_carry;
endgenerate
assign awvalid = stat==AW;
assign awlen = WBURST_LEN;
assign wvalid = stat==W;
assign wlast = w_cnt==WBURST_LEN;
assign wdata = awaddr;
assign bready = 1'b1;
assign arvalid = stat==AR;
assign arlen = RBURST_LEN;
assign rready = 1'b1;
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
{awaddr_carry, awaddr} <= '0;
w_cnt <= 8'd0;
araddr <= '0;
stat <= INIT;
end else begin
case(stat)
INIT: begin
{awaddr_carry, awaddr} <= '0;
w_cnt <= 8'd0;
araddr <= '0;
stat <= AW;
end
AW: if(awready) begin
w_cnt <= 8'd0;
stat <= W;
end
W: if(wready) begin
{awaddr_carry, awaddr} <= {awaddr_carry, awaddr} + (1<<D_LEVEL);
w_cnt <= w_cnt + 8'd1;
if(wlast)
stat <= B;
end
B: if(bvalid) begin
stat <= aw_end ? AR : AW;
end
AR: if(arready) begin
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;
if(araddr_next[A_WIDTH_TEST])
araddr <= '0;
end
end
endcase
end
// ------------------------------------------------------------
// read and write mismatch detect
// ------------------------------------------------------------
wire [D_WIDTH-1:0] rdata_idle = araddr;
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
error <= 1'b0;
error_cnt <= 16'd0;
end else begin
error <= rvalid && rready && rdata!=rdata_idle;
if(error)
error_cnt <= error_cnt + 16'd1;
end
endmodule

View File

@ -0,0 +1,177 @@
`timescale 1 ns/1 ns
module tb_ddr_ctrl();
// -------------------------------------------------------------------------------------
// DDR-SDRAM parameters
// -------------------------------------------------------------------------------------
localparam BA_BITS = 2;
localparam ROW_BITS = 13;
localparam COL_BITS = 11;
localparam DQ_LEVEL = 1;
localparam DQ_BITS = (4<<DQ_LEVEL);
localparam DQS_BITS = ((1<<DQ_LEVEL)+1)/2;
// -------------------------------------------------------------------------------------
// meta AXI4 burst length parameters
// -------------------------------------------------------------------------------------
localparam [7:0] WBURST_LEN = 8'd7;
localparam [7:0] RBURST_LEN = 8'd7;
// -------------------------------------------------------------------------------------
// meta AXI4 parameters
// -------------------------------------------------------------------------------------
localparam A_WIDTH = BA_BITS+ROW_BITS+COL_BITS+DQ_LEVEL-1;
localparam D_WIDTH = (8<<DQ_LEVEL);
// -------------------------------------------------------------------------------------
// driving clock and reset generate
// -------------------------------------------------------------------------------------
reg rstn=1'b0, clk300m=1'b1;
always #1.6667 clk300m = ~clk300m;
initial begin repeat(4) @(posedge clk300m); rstn<=1'b1; end
// -------------------------------------------------------------------------------------
// DDR-SDRAM signal
// -------------------------------------------------------------------------------------
wire ddr_ck_p, ddr_ck_n;
wire ddr_cke;
wire ddr_cs_n, ddr_ras_n, ddr_cas_n, ddr_we_n;
wire [ 1:0] ddr_ba;
wire [ROW_BITS-1:0] ddr_a;
wire [DQS_BITS-1:0] ddr_dm;
tri [DQS_BITS-1:0] ddr_dqs;
tri [ DQ_BITS-1:0] ddr_dq;
// -------------------------------------------------------------------------------------
// meta AXI4 interface
// -------------------------------------------------------------------------------------
wire aresetn;
wire aclk;
wire awvalid;
wire awready;
wire [A_WIDTH-1:0] awaddr;
wire [ 7:0] awlen;
wire wvalid;
wire wready;
wire wlast;
wire [D_WIDTH-1:0] wdata;
wire bvalid;
wire bready;
wire arvalid;
wire arready;
wire [A_WIDTH-1:0] araddr;
wire [ 7:0] arlen;
wire rvalid;
wire rready;
wire rlast;
wire [D_WIDTH-1:0] rdata;
// -------------------------------------------------------------------------------------
// self test error signal, 1'b1 indicates error
// -------------------------------------------------------------------------------------
wire error;
// -------------------------------------------------------------------------------------
// meta AXI4 master for testing
// -------------------------------------------------------------------------------------
axi_self_test_master #(
.A_WIDTH ( A_WIDTH ),
.A_WIDTH_TEST( 14 ),
.D_WIDTH ( D_WIDTH ),
.D_LEVEL ( DQ_LEVEL ),
.WBURST_LEN ( WBURST_LEN ),
.RBURST_LEN ( RBURST_LEN )
) axi_m_i (
.aresetn ( aresetn ),
.aclk ( aclk ),
.awvalid ( awvalid ),
.awready ( awready ),
.awaddr ( awaddr ),
.awlen ( awlen ),
.wvalid ( wvalid ),
.wready ( wready ),
.wlast ( wlast ),
.wdata ( wdata ),
.bvalid ( bvalid ),
.bready ( bready ),
.arvalid ( arvalid ),
.arready ( arready ),
.araddr ( araddr ),
.arlen ( arlen ),
.rvalid ( rvalid ),
.rready ( rready ),
.rlast ( rlast ),
.rdata ( rdata ),
.error ( error ),
.error_cnt ( )
);
// -------------------------------------------------------------------------------------
// DDR-SDRAM controller
// -------------------------------------------------------------------------------------
ddr_sdram_ctrl #(
.READ_BUFFER ( 0 ),
.BA_BITS ( BA_BITS ),
.ROW_BITS ( ROW_BITS ),
.COL_BITS ( COL_BITS ),
.DQ_LEVEL ( DQ_LEVEL ), // x8
.tREFC ( 10'd512 ),
.tW2I ( 8'd6 ),
.tR2I ( 8'd6 )
) ddr_sdram_ctrl_i (
.rstn ( rstn ),
.clk ( clk300m ),
.aresetn ( aresetn ),
.aclk ( aclk ),
.awvalid ( awvalid ),
.awready ( awready ),
.awaddr ( awaddr ),
.awlen ( awlen ),
.wvalid ( wvalid ),
.wready ( wready ),
.wlast ( wlast ),
.wdata ( wdata ),
.bvalid ( bvalid ),
.bready ( bready ),
.arvalid ( arvalid ),
.arready ( arready ),
.araddr ( araddr ),
.arlen ( arlen ),
.rvalid ( rvalid ),
.rready ( rready ),
.rlast ( rlast ),
.rdata ( rdata ),
.ddr_ck_p ( ddr_ck_p ),
.ddr_ck_n ( ddr_ck_n ),
.ddr_cke ( ddr_cke ),
.ddr_cs_n ( ddr_cs_n ),
.ddr_ras_n ( ddr_ras_n ),
.ddr_cas_n ( ddr_cas_n ),
.ddr_we_n ( ddr_we_n ),
.ddr_ba ( ddr_ba ),
.ddr_a ( ddr_a ),
.ddr_dm ( ddr_dm ),
.ddr_dqs ( ddr_dqs ),
.ddr_dq ( ddr_dq )
);
// -------------------------------------------------------------------------------------
// DDR-SDRAM simulation model
// -------------------------------------------------------------------------------------
ddr ddr_i (
.Clk ( ddr_ck_p ),
.Clk_n ( ddr_ck_n ),
.Cke ( ddr_cke ),
.Cs_n ( ddr_cs_n ),
.Ras_n ( ddr_ras_n ),
.Cas_n ( ddr_cas_n ),
.We_n ( ddr_we_n ),
.Ba ( ddr_ba ),
.Addr ( ddr_a ),
.Dm ( ddr_dm ),
.Dqs ( ddr_dqs ),
.Dq ( ddr_dq )
);
endmodule