SDRAM回环设计
文章目录
一、SDRAM简介
SDRAM英文全称"Synchronous Dynamic Random Access Memory",译为"同步动态随机存取内存"或"同步动态随机存储器",是动态随机存储器(Dynamic Random Access Memory,简称DRAM)家族的一份子。同步、动态、随机是其性能特点的外在说明,也是其区别其他存储器的特色标签。这三个概念性的标签,我们要好好理解掌握。
同步(Synchronous):与通常的异步DRAM不同, SDRAM存在一个同步接口,其工作时钟的时钟频率与对应控制器(CPU/FPGA)的时钟频率相同,并且SDRAM内部的命令发送与数据传输均以此时钟为基准,实现指令或数据的同步操作;
动态(Dynamic): SDRAM需要不断的刷新来保证存储阵列内数据不丢失;
随机(Random):数据在SDRAM中并不是按照线性依次存储,而是可以自由指定地址进行数据的读写。
空间存储量大、读写速度快以及价格相对便宜等优点使其在存储界屹立不倒、经久不衰,广泛应用在计算机中。随着时代的不断发展、技术的不断更新,SDRAM使用至今已过数十载,产品更新历经五代,分别是:第一代SDR SDRAM,第二代DDR SDRAM,第三代DDR2 SDRAM,第四代DDR3 SDRAM,第五代,DDR4 SDRAM。
第一代SDR SDRAM采用单端时钟信号,SDRAM只在时钟的上升沿进行数据采样;而后面的四代SDRAM由于工作频率比较快,所以采用可降低干扰的差分时钟信号作为同步时钟,双沿采样,速度更快,且功耗更低。同时技术的不断发展、制造工艺的不断提高,使得五代SDRAM的更新过程中,集成度越来越高、内核电压越来越低(SDR:3.3V 、DDR:2.5V、DDR2:1.8V、DDR3:1.5V、DDR4:1.2V),这也是SDRAM速度提高、功耗降低的重要原因。
本次运用的SDRAM为海力士公司的H57V2562GTR-75C,内存大小为256Mbit,有4个bank,每个bank有4M(8192行*512列)存储单元,每个单元可存16bit数据
1、引脚
信号 | 类型 | 描述 |
---|---|---|
clk | in | 时钟,所有其他输入在CLK的上升沿被寄存到SDRAM |
cke | in | 时钟使能,控制内部时钟信号,当禁用时,SDRAM将处于电源关闭、暂停或自刷新状态之一 |
cs_n | in | 片选,启用或禁用除CLK、CKE和DQM之外的所有输入 |
BA0,BA1 | in | bank地址线 |
A0-A12 | in | 行列地址线,行地址:RA0 ~ RA12,列地址:CA0 ~ CA8自动预充电标志:A10 |
RAS_n,CAS_n,WE_N | in | 行地址选通,列地址选通,写使能 |
LDQM,UDQM | I/O | 数据掩码,16位高低字节,可控制读写数据 |
DQ0-DQ15 | I/O | 数据输入输出 |
2、内部结构框图

由图可知,SDRAM内部包含一个状态机左上角state machine),外部通过控制CS_N、RAS_N、CAS_N、WE_N、A0-A12数据地址线以及BA0、BA1 BANK地址线来发送命令,命令经过解码器进行译码后,将控制参数保存到模式寄存器中,逻辑控制单元进而控制逻辑运行。
外部通过地址总线输入地址信息,地址信息在逻辑控制单元进行逻辑控制时起到辅助作用,除此之外,复用的地址总线与Bank控制逻辑、行地址复用器、列地址计数锁存器、列地址解码器等内部器件共同作用,精确选定存储阵列中与行列地址相对应的存储单元,进而进行数据存取操作。
DQM数据掩码线控制这数据是否有效,其又L低位与U高位两条线,每条控制着8bit数据。
DQ0-DQ15为数据线,其为双向,读写共用一条线。
3、操作指令

1、预充电(precharge)
预充电命令:关闭特定bank中激活的行,或关闭所有bank中激活的行。
A10决定预充电模式:
当预充电命令中的地址A10为高时,预充电所有bank;当预充电命令中A10为低时,预充电特定bank。
当write/read命令中的A10为高时,自动预充电(auto-precharge)被使能;反之。
预充电命令发送之后,这些bank等待tRP才能接收命令
2、自动预充电(auto-precharge)
自动预充电是非显示命令,即使能自动预充电是需要发送write/read命令时将地址中A10拉高,在读写
突发结束后,立即预充电那个bank/row。
3、NOP
NOP 指令用以表明对 sdram 芯片(CS# == 0)进行空操作。NOP 指令的目的是在 sdram 在空闲
或者等待状态下,避免去执行一些潜在的不需要的指令。已经在执行过程中的指令不受影响。
4、自动刷新(auto-refresh)与自刷新(self-refresh)
为使数据不丢失,电容的两次刷新时间不能超过64ms,刷新都是针对行的。共性:都不需要外部提供地址信息,SDRAM内部有一个行地址生成器(刷新计数器)。刷新都是针对
一行的,不需要对列地址寻址,但也不需要对行进行寻址,因为内部有刷新计数器
自动刷新:SDRAM正常工作模式中为了数据不丢失进行的操作,需要外部时钟参与,刷新的行地址也
是由内部刷新计算器控制
自刷新:休眠模式低功耗状态下存储数据,不需要外部时钟参与,刷新的行地址内部刷新计算器控
制。
发送自动刷新命令需要的时间为tRRC(自动刷新周期),由于是对行操作,等效于行选通时间(RAS)
SDRAM中每次刷新操作所需要的时间为自动刷新周期(tRC),在自动刷新指令发出后需要等待tRC才能发
送其他指令。
5、行激活
行激活命令也叫做bank激活命令,作用是在指定bank中激活一行;
行激活后会一直处于激活状态(即列寻址处于激活状态),直到预充电命令被发送到这个bank;
行激活命令之后,需要延时tRCD(即发出行地址到发出列地址的时间间隔),才能发送READ/WRITE命令(读写操作必须先激活对应bank)
6、读操作
读数据命令用来开启数据的突发读,bank,row都可选,注意A10的值决定是否执行自动预充电操作,若执行自动预充电,突发读结束后就进行预充电,此行关闭,若不执行自动预充电,该行保持激活,仍能被访问。
CAS latency :读延迟(读潜伏周期,CL)Burst length:突发长度(BL)
读命令发出后,输出buffer(理解为SDRAM的dq_out)会在(CL-1)个时钟后期后变为低阻,然后会在突发读结束后重新变为高阻态,
7、写操作
DM(数据掩码)高有效,当为低时,数据能正确被写入DM;当为高时,数据将被忽略。
写突发时,第一个数据与写命令同步;
突发写-->预充电中间需要间隔tDPL。
二、系统设计
所实现功能 :pc发送数据,存入fifo的数据达到所设阈值,进行突发写操作(此处用的ip核不支持多bit突发读写操作,故在控制模块中实现突发读写操作),当按键(经过消抖)按下,进行突发读操作,将读取的数据发送回pc显示出来。
三、实现流程
1、SDRAM接口

SDRAM接口部分由ip核实现
1) 选择Platform Designer
2) 在左上角处搜索sdram,在下方双击选中ip添加
3) 配置各个参数
本次运用的SDRAM的数据宽度为16bit,有4个bank,每个bank有13行9列

参数 | 含义 | 典型值意义 |
---|---|---|
CAS latency cycles | 读命令发出后,到第一个有效数据出现在总线上的时钟周期数 | 设为 3 → 3 个时钟 |
Initialization refresh cycles | 上电后、正式初始化前,控制器自动发出的 Auto-Refresh 命令次数 | 8 次 |
Issue one refresh command every | 两次刷新命令之间的间隔时间 | 7.8 µs(SDRAM 要求 64 ms 内刷完所有行,因此 7.8 µs/行≈8192 行) |
Delay after powerup, before initialization | 芯片上电稳定后再开始初始化的等待时间 | 200 µs(JEDEC 规范建议 ≥100 µs) |
Duration of refresh command (t_rfc) | 单个 Auto-Refresh 命令占用总线的最短时间 | 70 ns |
Duration of precharge command (t_rp) | 关闭当前行(Precharge)所需时间 | 20 ns |
ACTIVE to READ or WRITE delay (t_rcd) | 行激活到列读/写命令之间的最小间隔 | 20 ns |
Access time (t_ac) | 时钟沿到数据有效输出的延迟(器件参数,非用户设) | 5 ns |
Write recovery time (t_wr) | 写命令结束后到允许 Precharge 的最小间隔 | 20 ns |
配置完如下
4) 点击右下角finish左边按钮出现以下界面

改为生成verilog代码,路径自行选择,更改完成点击生成,等待完成,等待时间因电脑配置有差异

生成完成,依次点击close、finish
5) finish后出现以下界面提示添加文件

添加完成在ip查看界面才会有显示

生成的例化模板:
c
sdram_ip u0 (
.clk_clk (), // clk.clk
.reset_reset_n (), // reset.reset_n
.sdram_addr (), // sdram.addr
.sdram_ba (), // .ba
.sdram_cas_n (), // .cas_n
.sdram_cke (), // .cke
.sdram_cs_n (), // .cs_n
.sdram_dq (), // .dq
.sdram_dqm (), // .dqm
.sdram_ras_n (), // .ras_n
.sdram_we_n (), // .we_n
.avs_address (), // avs.address
.avs_byteenable_n (), // .byteenable_n
.avs_chipselect (), // .chipselect
.avs_writedata (), // .writedata
.avs_read_n (), // .read_n
.avs_write_n (), // .write_n
.avs_readdata (), // .readdata
.avs_readdatavalid (), // .readdatavalid
.avs_waitrequest () // .waitrequest
);
端口名称 | 方向 | 位宽 | 含义说明 |
---|---|---|---|
clk_clk | input | 1 | 全局系统时钟,用于 SDRAM 控制器内部逻辑以及 SDRAM 芯片。 |
reset_reset_n | input | 1 | 全局异步复位,低有效。 |
sdram_addr[12:0] | output | 13 | SDRAM 地址总线,行列地址复用。 |
sdram_ba[1:0] | output | 2 | Bank 地址,选择 SDRAM 的 4 个 bank。 |
sdram_cas_n | output | 1 | 列地址选通,低有效。 |
sdram_cke | output | 1 | 时钟使能,高时 SDRAM 响应时钟。 |
sdram_cs_n | output | 1 | 片选,低有效。 |
sdram_dq[15:0] | inout | 16 | 双向数据总线。 |
sdram_dqm[1:0] | output | 2 | 数据掩码/字节使能,对应 16bit 数据的高低字节。 |
sdram_ras_n | output | 1 | 行地址选通,低有效。 |
sdram_we_n | output | 1 | 写使能,低有效。 |
avs_address[21:0] | input | 22 | Avalon-MM 总线地址(字节地址)。 |
avs_byteenable_n[1:0] | input | 2 | 字节使能,低有效,对应 16bit 数据高低字节。 |
avs_chipselect | input | 1 | Avalon-MM 片选,高有效。 |
avs_writedata[15:0] | input | 16 | 写数据。 |
avs_read_n | input | 1 | 读请求,低有效。 |
avs_write_n | input | 1 | 写请求,低有效。 |
avs_readdata[15:0] | output | 16 | 读回数据。 |
avs_readdatavalid | output | 1 | 读数据有效指示,高有效。 |
avs_waitrequest | output | 1 | 控制器忙,拉低时表示需要等待。 |
sdram前缀为输出给sdram芯片的接口,avs前缀为内部的控制的Avalon协议接口。
2、FIFO设置
c
//---------<写FIFO例化>-------------------------------------------------
wr_fifo wr_fifo_inst (
.aclr ( ~rst_n ),
.data ( {2{rx_data}} ),
.rdclk ( clk ),
.rdreq ( wr_rden ),
.wrclk ( clk_in ),
.wrreq ( wr_wren ),
.q ( wr_q ),
.rdempty ( wr_rdempty ),
.rdusedw ( wr_rdusedw ),
.wrfull ( wr_wrfull )
);
assign wr_wren = ~wr_wrfull && rx_vld;
assign wr_rden = ~wr_rdempty && (state_c == WRITE) && ~avm_waitrequest;
由于所设置SDRAM宽度为16,但uart_rx传入为8bit,所以拼两个传入的数据存入SDRAM。
写使能(wr_wren):fifo未满且rx传入的数据有效,就将数据写入fifo
读使能(wr_rden):fifo不空且处于写状态,且sdram处于不忙状态(waitrequest为低)将数据传给sdram
c
//---------<读FIFO例化>-------------------------------------------------
rd_fifo rd_fifo_inst (
.aclr ( ~rst_n ),
.data ( avm_readdata ),
.rdclk ( clk_out ),
.rdreq ( rd_rden ),
.wrclk ( clk ),
.wrreq ( rd_wren ),
.q ( rd_q ),
.rdempty ( rd_rdempty ),
.wrfull ( rd_wrfull )
);
assign rd_wren = ~rd_wrfull && avm_readdatavalid;
assign rd_rden = ~rd_rdempty && tx_done;
写使能(rd_wren):fifo未满且sdram处于不忙状态(waitrequest为低)将数据传给读fifo
读使能(rd_rden):fifo不空且tx发送模块不忙,将数据传给tx发送到pc
3、内部SDRAM的控制模块

由于uart时钟与sdram的时钟不同,这里用了两个异步fifo,一个用于写,一个用于读;wrfifo写侧为uart_rx输入的系统时钟,即为clk_in,读侧为输出到sdram的时钟,为clk_100m;rdfifo写侧为sdram传出的数据,时钟为clk_100m,读侧为通过uart_tx传回pc的数据接口,时钟为系统时钟。
在此设置一个简单的状态机实现读写操作,此处的burst_lenth为 10。
c
//---------<突发计数器>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_burst <= 'd0;
end
else if(add_cnt_burst)begin
if(end_cnt_burst)begin
cnt_burst <= 'd0;
end
else begin
cnt_burst <= cnt_burst + 1'b1;
end
end
end
assign add_cnt_burst = ((state_c == READ) || (state_c == WRITE)) && ~avm_waitrequest;
assign end_cnt_burst = add_cnt_burst && cnt_burst == (BURST_LENTH-1);

当按键按下进入读数据状态,开始从sdram读取数据,当读fifo中读了10个数据时跳出。
当写fifo的数据大于所设突发操作的阈值,就进行写入操作,跳转到write状态,将10个数据写入sdram,写完10个后跳到空闲状态。
读写地址通过计数器实现
c
//---------<读写地址>-------------------------------------------------
//写地址计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_addr <= 'd0;
end
else if(add_wr_addr)begin
if(end_wr_addr)begin
wr_addr <= 'd0;
end
else begin
wr_addr <= wr_addr + 1'b1;
end
end
end
assign add_wr_addr = (state_c == WRITE) && ~avm_waitrequest;
assign end_wr_addr = add_wr_addr && wr_addr == 24'hff_ff_ff-1;
//读地址计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_addr <= 'd0;
end
else if(add_rd_addr)begin
if(end_rd_addr)begin
rd_addr <= 'd0;
end
else begin
rd_addr <= rd_addr + 1'b1;
end
end
end
assign add_rd_addr = (state_c == READ) && ~avm_waitrequest;
assign end_rd_addr = add_rd_addr && rd_addr == 24'hff_ff_ff-1;
由于地址线共用,所以要判断现在是读状态还是写状态
c
//---------<状态判断>-------------------------------------------------
assign avm_address = (state_c == WRITE) ? {wr_addr[23],wr_addr[21:9],wr_addr[22],wr_addr[8:0]} :
{rd_addr[23],rd_addr[21:9],rd_addr[22],rd_addr[8:0]} ;
其他输出
c
assign tx_data = rd_q [7:0];
assign tx_vld = rd_rden;
assign avm_writedata = wr_q;
assign avm_read_n = ~(state_c == READ);
assign avm_write_n = ~(state_c == WRITE);
4、其他
其他模块复用之前的。
四、实现效果

直接按下按键读出错乱的数据,按下复位,发送数据,将数据写入sdram

按下两次按键读出数据

读出的20个数据与发送的前20个相同,验证成功。
五、总结
相较于之前的几个存储器来说,sdram是并行的,且有其他的各种信号,端口较多比较复杂,且内部要用avalon协议传输数据,连线和数据之间的传输较为复杂,需要认真理解,本实验主要目的为理解sdram的操作和工作原理。
六、代码
1、top
c
/*******************************************************************
Engineer: Turing_kun
Create Date: 2025-07-30 14:03:14
Description: 项目顶层
********************************************************************/
module top (
input clk ,
input rst_n ,
input [1:0] sw ,
//---------<sdram>-------------------------------------------------
output [12:0] sdram_addr ,
output [1:0] sdram_ba ,
output sdram_cas_n ,
output sdram_cke ,
output sdram_cs_n ,
inout [15:0] sdram_dq ,
output [1:0] sdram_dqm ,
output sdram_ras_n ,
output sdram_we_n ,
output sdram_clk ,
//---------<uart>-------------------------------------------------
input rx ,
output tx ,
//---------<key>-------------------------------------------------
input [0:0] key_in
);
wire clk_100ms;
wire clk_100ms_s;
wire locked;
//---------<uart_rx>-------------------------------------------------
wire [7:0] rx_data;
wire rx_vld;
//---------<uart_tx>-------------------------------------------------
wire [7:0] tx_data;
wire tx_vld;
wire tx_done;
//---------<key>-------------------------------------------------
wire key_down;
assign sdram_clk = clk_100ms_s;
sdram_top inst_sdram_top(
.clk (clk_100ms ),
.clk_in (clk ),
.clk_out (clk ),
.rst_n (rst_n ),
.rx_data (rx_data ),
.rx_vld (rx_vld ),
.tx_done (tx_done ),
.tx_data (tx_data ),
.tx_vld (tx_vld ),
.key_down (key_down ),
.sdram_addr (sdram_addr ),
.sdram_ba (sdram_ba ),
.sdram_cas_n(sdram_cas_n),
.sdram_cke (sdram_cke ),
.sdram_cs_n (sdram_cs_n ),
.sdram_dq (sdram_dq ),
.sdram_dqm (sdram_dqm ),
.sdram_ras_n(sdram_ras_n),
.sdram_we_n (sdram_we_n )
);
sdram_pll sdram_pll_inst (
.areset ( ~rst_n ),
.inclk0 ( clk ),
.c0 ( clk_100ms ),
.c1 ( clk_100ms_s ),
.locked ( locked )
);
uart_rx inst_uart_rx(
.clk (clk ),
.rst_n (rst_n ),
.rx (rx ),
.sw (sw ),
.rx_data (rx_data ),
.rx_done (rx_vld )
);
uart_tx inst_uart_tx(
.clk (clk ),
.rst_n (rst_n ),
.tx_data (tx_data ),
.tx_start(tx_vld ),
.sw (sw ),
.tx (tx ),
.tx_done (tx_done )
);
fsm_key inst_fsm_key(
.clk (clk ),
.rst_n (rst_n ),
.key_in (key_in ),
.key_down (key_down)
);
endmodule
2、sdram_top
c
/*******************************************************************
Engineer: Turing_kun
Create Date: 2025-07-30 14:02:40
Description: SDRAM子顶层
********************************************************************/
module sdram_top(
input clk , //100mHz
input clk_in , //50mHz
input clk_out , //50mHz
input rst_n ,
//---------<uart_rx>-------------------------------------------------
input [7:0] rx_data ,
input rx_vld ,
//---------<uart_tx>-------------------------------------------------
input tx_done ,
output [7:0] tx_data ,
output tx_vld ,
//---------<key>-------------------------------------------------
input key_down ,
//---------<sdram_ip>-------------------------------------------------
output [12:0] sdram_addr ,
output [1:0] sdram_ba ,
output sdram_cas_n ,
output sdram_cke ,
output sdram_cs_n ,
inout [15:0] sdram_dq ,
output [1:0] sdram_dqm ,
output sdram_ras_n ,
output sdram_we_n
);
wire [23:0] avm_address ;
wire [15:0] avm_writedata ;
wire avm_read_n ;
wire avm_write_n ;
wire [15:0] avm_readdata ;
wire avm_readdatavalid ;
wire avm_waitrequest ;
sdram_ctrl inst_sdram_ctrl(
.clk (clk ),
.clk_in (clk_in ),
.clk_out (clk_out ),
.rst_n (rst_n ),
.rx_data (rx_data ),
.rx_vld (rx_vld ),
.tx_done (tx_done ),
.tx_data (tx_data ),
.tx_vld (tx_vld ),
.key_down (key_down ),
.avm_address (avm_address ),
.avm_writedata (avm_writedata ),
.avm_read_n (avm_read_n ),
.avm_write_n (avm_write_n ),
.avm_readdata (avm_readdata ),
.avm_readdatavalid(avm_readdatavalid),
.avm_waitrequest (avm_waitrequest )
);
sdram_ip inst_sdram_ip(
.avs_address (avm_address ),
.avs_byteenable_n (2'b00 ),
.avs_chipselect (1'b1 ),
.avs_writedata (avm_writedata ),
.avs_read_n (avm_read_n ),
.avs_write_n (avm_write_n ),
.avs_readdata (avm_readdata ),
.avs_readdatavalid(avm_readdatavalid),
.avs_waitrequest (avm_waitrequest ),
.clk_clk (clk ),
.reset_reset_n (rst_n ),
.sdram_addr (sdram_addr ),
.sdram_ba (sdram_ba ),
.sdram_cas_n (sdram_cas_n ),
.sdram_cke (sdram_cke ),
.sdram_cs_n (sdram_cs_n ),
.sdram_dq (sdram_dq ),
.sdram_dqm (sdram_dqm ),
.sdram_ras_n (sdram_ras_n ),
.sdram_we_n (sdram_we_n )
);
endmodule
3、sdram_ctrl
c
/*******************************************************************
Engineer: Turing_kun
Create Date: 2025-07-30 14:05:40
Description: SDRAM控制模块
********************************************************************/
module sdram_ctrl #(parameter BURST_LENTH = 10)(
input clk , //100mHz
input clk_in , //50mHz
input clk_out , //50mHz
input rst_n ,
//---------<uart_rx>---------------------------------
input [7:0] rx_data ,
input rx_vld ,
//---------<uart_tx>---------------------------------
input tx_done ,
output [7:0] tx_data ,
output tx_vld ,
//---------<key>-------------------------------------
input key_down ,
//---------<sdram_ip>-------------------------------------------------
output [23:0] avm_address ,
output [15:0] avm_writedata ,
output avm_read_n ,
output avm_write_n ,
input [15:0] avm_readdata ,
input avm_readdatavalid ,
input avm_waitrequest
);
//rd_fifo
wire rd_rden ;
wire rd_wren ;
wire [15:0] rd_q ;
wire rd_rdempty ;
wire rd_wrfull ;
//wr_fifo
wire wr_rden ;
wire wr_wren ;
wire [15:0] wr_q ;
wire wr_rdempty ;
wire wr_wrfull ;
wire [8:0] wr_rdusedw ;
//---------<状态参数>-------------------------------------------------
reg [1:0] state_c ;
reg [1:0] state_n ;
localparam IDLE = 2'd0,
READ = 2'd1,
WRITE = 2'd2,
DONE = 2'd3;
wire IDLE_2_READ;
wire READ_2_DONE;
wire IDLE_2_WRITE;
wire WRITE_2_DONE;
reg [8:0] cnt_burst ;
wire add_cnt_burst ;
wire end_cnt_burst ;
//写地址
reg [23:0] wr_addr ;
wire add_wr_addr;
wire end_wr_addr;
//读地址
reg [23:0] rd_addr ;
wire add_rd_addr;
wire end_rd_addr;
//---------<读FIFO例化>-------------------------------------------------
rd_fifo rd_fifo_inst (
.aclr ( ~rst_n ),
.data ( avm_readdata ),
.rdclk ( clk_out ),
.rdreq ( rd_rden ),
.wrclk ( clk ),
.wrreq ( rd_wren ),
.q ( rd_q ),
.rdempty ( rd_rdempty ),
.wrfull ( rd_wrfull )
);
assign rd_wren = ~rd_wrfull && avm_readdatavalid;
assign rd_rden = ~rd_rdempty && tx_done;
//---------<写FIFO例化>-------------------------------------------------
wr_fifo wr_fifo_inst (
.aclr ( ~rst_n ),
.data ( {2{rx_data}} ),
.rdclk ( clk ),
.rdreq ( wr_rden ),
.wrclk ( clk_in ),
.wrreq ( wr_wren ),
.q ( wr_q ),
.rdempty ( wr_rdempty ),
.rdusedw ( wr_rdusedw ),
.wrfull ( wr_wrfull )
);
assign wr_wren = ~wr_wrfull && rx_vld;
assign wr_rden = ~wr_rdempty && (state_c == WRITE) && ~avm_waitrequest;
//---------<突发计数器>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_burst <= 'd0;
end
else if(add_cnt_burst)begin
if(end_cnt_burst)begin
cnt_burst <= 'd0;
end
else begin
cnt_burst <= cnt_burst + 1'b1;
end
end
end
assign add_cnt_burst = ((state_c == READ) || (state_c == WRITE)) && ~avm_waitrequest;
assign end_cnt_burst = add_cnt_burst && cnt_burst == (BURST_LENTH-1);
//---------<state>-------------------------------------------------
//第一段:同步时序描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
//第二段:组合逻辑判断状态转移条件,描述状态转移规律
always @(*) begin
case(state_c)
IDLE : begin
if(IDLE_2_READ)
state_n = READ;
else if(IDLE_2_WRITE)
state_n = WRITE;
else
state_n = state_c;
end
READ : state_n = (READ_2_DONE ) ? DONE : state_c;
WRITE : state_n = (WRITE_2_DONE) ? DONE : state_c;
DONE : state_n = IDLE;
default : state_n = IDLE;
endcase
end
assign IDLE_2_READ = (state_c == IDLE ) && key_down;
assign READ_2_DONE = (state_c == READ ) && end_cnt_burst;
assign IDLE_2_WRITE = (state_c == IDLE ) && wr_rdusedw >= BURST_LENTH;
assign WRITE_2_DONE = (state_c == WRITE) && end_cnt_burst;
//---------<读写地址>-------------------------------------------------
//写地址计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_addr <= 'd0;
end
else if(add_wr_addr)begin
if(end_wr_addr)begin
wr_addr <= 'd0;
end
else begin
wr_addr <= wr_addr + 1'b1;
end
end
end
assign add_wr_addr = (state_c == WRITE) && ~avm_waitrequest;
assign end_wr_addr = add_wr_addr && wr_addr == 24'hff_ff_ff-1;
//读地址计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_addr <= 'd0;
end
else if(add_rd_addr)begin
if(end_rd_addr)begin
rd_addr <= 'd0;
end
else begin
rd_addr <= rd_addr + 1'b1;
end
end
end
assign add_rd_addr = (state_c == READ) && ~avm_waitrequest;
assign end_rd_addr = add_rd_addr && rd_addr == 24'hff_ff_ff-1;
//---------<tx_data tx_vld>-------------------------------------------------
assign tx_data = rd_q [7:0];
assign tx_vld = rd_rden;
//---------<状态判断>-------------------------------------------------
assign avm_address = (state_c == WRITE) ? {wr_addr[23],wr_addr[21:9],wr_addr[22],wr_addr[8:0]} :
{rd_addr[23],rd_addr[21:9],rd_addr[22],rd_addr[8:0]} ;
assign avm_writedata = wr_q;
assign avm_read_n = ~(state_c == READ);
assign avm_write_n = ~(state_c == WRITE);
endmodule
其他复用之前的项目