基于FPGA的实时图像处理系统(1)——SDRAM回环测试
SDRAM回环设计
文章目录
- SDRAM回环设计
- 一、SDRAM简介
- 1、引脚
- 2、内部结构框图
- 3、操作指令
- 二、系统设计
- 三、实现流程
- 1、SDRAM接口
- 2、FIFO设置
- 3、内部SDRAM的控制模块
- 4、其他
- 四、实现效果
- 五、总结
- 六、代码
- 1、top
- 2、sdram_top
- 3、sdram_ctrl
一、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查看界面才会有显示
生成的例化模板:
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设置
//---------<写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
//---------<读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。
//---------<突发计数器>------------------------------------------------- always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_burst <= 'd0;end else if(add_cnt_burst)begin if(end_cnt_burst)begin cnt_burst <= 'd0;endelse 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个后跳到空闲状态。
读写地址通过计数器实现
//---------<读写地址>-------------------------------------------------
//写地址计数
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginwr_addr <= 'd0;end else if(add_wr_addr)begin if(end_wr_addr)begin wr_addr <= 'd0;endelse 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)beginrd_addr <= 'd0;end else if(add_rd_addr)begin if(end_rd_addr)begin rd_addr <= 'd0;endelse 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;
由于地址线共用,所以要判断现在是读状态还是写状态
//---------<状态判断>-------------------------------------------------
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 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
/*******************************************************************
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
/*******************************************************************
Engineer: Turing_kun
Create Date: 2025-07-30 14:02:40
Description: SDRAM子顶层
********************************************************************/module sdram_top( input clk , //100mHzinput clk_in , //50mHzinput clk_out , //50mHzinput 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
/*******************************************************************
Engineer: Turing_kun
Create Date: 2025-07-30 14:05:40
Description: SDRAM控制模块
********************************************************************/module sdram_ctrl #(parameter BURST_LENTH = 10)(input clk , //100mHzinput clk_in , //50mHzinput clk_out , //50mHzinput 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)begincnt_burst <= 'd0;end else if(add_cnt_burst)begin if(end_cnt_burst)begin cnt_burst <= 'd0;endelse 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)beginstate_c <= IDLE;end else begin state_c <= state_n;end
end//第二段:组合逻辑判断状态转移条件,描述状态转移规律
always @(*) begincase(state_c)IDLE : beginif(IDLE_2_READ)state_n = READ;else if(IDLE_2_WRITE)state_n = WRITE;else state_n = state_c;endREAD : 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
endassign 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)beginwr_addr <= 'd0;end else if(add_wr_addr)begin if(end_wr_addr)begin wr_addr <= 'd0;endelse 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)beginrd_addr <= 'd0;end else if(add_rd_addr)begin if(end_rd_addr)begin rd_addr <= 'd0;endelse 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
其他复用之前的项目