想看我之前关于sdram的看我之前的博客
一、状态机设计
控制模块的状态机也就idle,read,write,done这几个基本的状态
我发现现在写这些模块,没有状态机不舒服
二、代码部分
1.sdram_interface.v
2.sdram_control
module sdram_control(
input clk,
input clk_100m,
input rst_n,
//sdram接口和control
input [15:0] din,// 从sdram来的数据
input din_vld,
output [15:0] dout,// 给sdram的数据
output [23:0] brc_address,// bank row col 地址 2+13+9
output reg rd_req,// 读请求
output reg wr_req,// 写请求
input busy,// 接口busy
input ack,// 接口回应
//sdram control和用户接口 串口
input rd_en,
input [7:0] rx_byte,
input rx_byte_vld,
output [7:0] tx_byte,
output tx_byte_vld,
input tx_busy
);
parameter BURST_MAX_LENGTH = 24'h1fffff,
BURST_LENGTH = 8;
localparam IDLE = 4'b0001,
READ = 4'b0010,
WRITE = 4'b0100,
DONE = 4'b1000;
reg [3:0] state_c;
reg [3:0] state_n;
wire idle2read ;
wire idle2write;
wire read2done ;
wire write2done;
wire done2idle ;
// 读地址计数器
reg [23:0] cnt_rdadd;
wire add_cnt_rdadd;
wire end_cnt_rdadd;
// 写地址计数器
reg [23:0] cnt_wradd;
wire add_cnt_wradd;
wire end_cnt_wradd;
// 读写标志
// 0代表写,1代表读
reg rw_flag;
// 寄存接口响应ack 打一拍
reg ack_r;
// 检测ack的下降沿
wire ack_nedge;
// 写fifo
wire wrfifo_rdreq ;
wire wrfifo_wrreq ;
wire [15:0] wrfifo_qout ;
wire wrfifo_rdempty;
wire wrfifo_rdfull ;
wire [11:0] wrfifo_rdusedw;
wire wrfifo_wrempty;
wire wrfifo_wrfull ;
wire [12:0] wrfifo_wrusedw;
// 读fifo
wire rdfifo_rdreq ;
wire rdfifo_wrreq ;
wire [7:0] rdfifo_qout ;
wire rdfifo_rdempty;
wire rdfifo_rdfull ;
wire [13:0] rdfifo_rdusedw;
wire rdfifo_wrempty;
wire rdfifo_wrfull ;
wire [12:0] rdfifo_wrusedw;
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(idle2read)begin
state_n = READ;
end
else if(idle2write)begin
state_n = WRITE;
end
else begin
state_n = state_c;
end
end
READ :begin
if(read2done)begin
state_n = DONE;
end
else begin
state_n = state_c;
end
end
WRITE :begin
if(write2done)begin
state_n = DONE;
end
else begin
state_n = state_c;
end
end
DONE :begin
if(done2idle)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
default: state_n = IDLE;
endcase
end
assign idle2read = state_c == IDLE && (~busy) && (rd_en);
assign idle2write = state_c == IDLE && (~busy) && (wrfifo_rdusedw >= BURST_LENGTH);
assign read2done = state_c == READ && (ack_nedge);
assign write2done = state_c == WRITE && (ack_nedge);
assign done2idle = state_c == DONE && (1'b1);
// rw_flag读写标志
// 写一次读一次
// 0代表写,1代表读
// always @(posedge clk or negedge rst_n)begin
// if(!rst_n)begin
// rw_flag <= 1'b0;
// end
// else if(idle2write)begin
// rw_flag <= 1'b0;
// end
// else if(write2done)begin
// rw_flag <= 1'b1;
// end
// else if(idle2read)begin
// rw_flag <= 1'b1;
// end
// else if(read2done)begin
// rw_flag <= 1'b0;
// end
// end
// 接口响应ack 打一拍
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
ack_r <= 0;
end
else begin
ack_r <= ack;
end
end
// 检测ack的下降沿
assign ack_nedge = ~ack & ack_r;
// 读地址计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_rdadd <= 0;
end
else if(add_cnt_rdadd)begin
if(end_cnt_rdadd)begin
cnt_rdadd <= 0;
end
else begin
cnt_rdadd <= cnt_rdadd + BURST_LENGTH;
end
end
else begin
cnt_rdadd <= cnt_rdadd;
end
end
assign add_cnt_rdadd = read2done;
assign end_cnt_rdadd = add_cnt_rdadd && cnt_rdadd == BURST_MAX_LENGTH - BURST_LENGTH;
// 写地址计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_wradd <= 0;
end
else if(add_cnt_wradd)begin
if(end_cnt_wradd)begin
cnt_wradd <= 0;
end
else begin
cnt_wradd <= cnt_wradd + BURST_LENGTH;
end
end
else begin
cnt_wradd <= cnt_wradd;
end
end
assign add_cnt_wradd = write2done;
assign end_cnt_wradd = add_cnt_wradd && cnt_wradd == BURST_MAX_LENGTH - BURST_LENGTH;
// 读写请求
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_req <= 0;
wr_req <= 0;
end
else if(idle2write)begin
wr_req <= 1'b1;
end
else if(idle2read)begin
rd_req <= 1'b1;
end
else begin
wr_req <= 1'b0;
rd_req <= 1'b0;
end
end
// 写fifo
// 从串口接收数据发给sdram
wrfifo wrfifo_inst (
.data ( rx_byte ),
.rdclk ( clk_100m ),
.rdreq ( wrfifo_rdreq ),
.wrclk ( clk ),
.wrreq ( wrfifo_wrreq ),
.q ( wrfifo_qout ),
.rdempty( wrfifo_rdempty ),
.rdfull ( wrfifo_rdfull ),
.rdusedw( wrfifo_rdusedw ),
.wrempty( wrfifo_wrempty ),
.wrfull ( wrfifo_wrfull ),
.wrusedw( wrfifo_wrusedw )
);
assign wrfifo_rdreq = (~wrfifo_rdempty) && ack && (state_c == WRITE);
assign wrfifo_wrreq = (~wrfifo_wrfull) && rx_byte_vld;
assign dout = wrfifo_qout;
// 读fifo
// 从sdram接收数据发给串口
rdfifo rdfifo_inst (
.data ( din ),
.rdclk ( clk ),
.rdreq ( rdfifo_rdreq ),
.wrclk ( clk_100m ),
.wrreq ( rdfifo_wrreq ),
.q ( rdfifo_qout ),
.rdempty( rdfifo_rdempty ),
.rdfull ( rdfifo_rdfull ),
.rdusedw( rdfifo_rdusedw ),
.wrempty( rdfifo_wrempty ),
.wrfull ( rdfifo_wrfull ),
.wrusedw( rdfifo_wrusedw )
);
assign rdfifo_rdreq = (~rdfifo_rdempty) && (~tx_busy);
assign rdfifo_wrreq = (~rdfifo_wrfull) && (state_c == READ) && din_vld;
assign tx_byte = rdfifo_qout;
// bank row col 地址
assign brc_address = (state_c == WRITE)?cnt_wradd:((state_c == READ)?cnt_rdadd:0);
// assign brc_address = 24'b0;
// 读请求
// assign rd_req = (state_c == READ) ? 1 : 0;
// 写请求
// assign wr_req = (state_c == WRITE) ? 1 :0;
// 串口数据有效
assign tx_byte_vld = rdfifo_rdreq;
endmodule
3.top.v
module top(
input clk,
input rst_n,
input [0:0] key_in,
// 串口uart
input uart_rx,
output uart_tx,
// sdram
output sdram_clk ,
output sdram_cke ,
output sdram_cs_n ,
output [1:0] sdram_bank ,
output [12:0] sdram_address,
output sdram_ras_n ,
output sdram_cas_n ,
output sdram_we_n ,
output [1:0] sdram_dqm ,
inout [15:0] sdram_dq
);
// 中间信号
wire clk_100m;
wire clk_100m_75deg;
wire key_out;
wire [15:0] rx_byte;
wire rx_byte_vld;
wire [15:0] tx_byte;
wire tx_byte_vld;
wire tx_busy;
wire [15:0] sdram_din;
wire sdram_din_vld;
wire [15:0] sdram_dout;
wire [23:0] brc_address;
wire rd_req;
wire wr_req;
wire busy;
wire ack;
wire [15:0] sdram_dq_in ;
wire [15:0] sdram_dq_out ;
wire sdram_dq_out_en;
assign sdram_dq_in = sdram_dq;
assign sdram_dq = sdram_dq_out_en?sdram_dq_out:16'hzzzz;
// pll锁相环
pll pll_inst (
.areset ( ~rst_n ),
.inclk0 ( clk ),
.c0 ( clk_100m ),
.c1 ( clk_100m_75deg ),
.locked ( locked_sig )
);
// 串口接收模块
uart_rx u_uart_rx(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input */.baud_sel (1),// 波特率的选择
/* input */.din (uart_rx ),// 串口接收模块接收到主机来的1bit的数据
/* output [7:0] */.dout (rx_byte ),// 串口接收模块串并转换的数据发送
/* output */.dout_vld (rx_byte_vld)
);
uart_tx u_uart_tx(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input */.baud_sel (1),// 波特率的选择
/* input [7:0] */.din (tx_byte ),// 串并转换的数据
/* input */.din_vld (tx_byte_vld ),// 串并转换的数据有效
/* output */.dout (uart_tx ),// 发送模块发送的1bit数据
/* output */.busy (tx_busy ) // 发送模块忙标志
);
key_filter u_key_filter(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input [KEY_W-1:0] */.key_in (key_in ),
/* output reg [KEY_W-1:0] */.key_out (key_out)
);
sdram_control u_sdram_control(
/* input */.clk (clk ),
/* input */.clk_100m (clk_100m ),
/* input */.rst_n (rst_n ),
/* input [15:0] */.din (sdram_din ),// 从sdram来的数据
/* input */.din_vld (sdram_din_vld),
/* output [15:0] */.dout (sdram_dout ),// 给sdram的数据
/* output [23:0] */.brc_address (brc_address),// bank row col 地址 2+13+9
/* output */.rd_req (rd_req ),// 读请求
/* output */.wr_req (wr_req ),// 写请求
/* input */.busy (busy ),// 接口busy
/* input */.ack (ack ),// 接口回应
/* input */.rd_en (key_out ),
/* input [7:0] */.rx_byte (rx_byte ),
/* input */.rx_byte_vld (rx_byte_vld),
/* output [7:0] */.tx_byte (tx_byte ),
/* output */.tx_byte_vld (tx_byte_vld),
/* input */.tx_busy (tx_busy )
);
sdram_interface u_sdram_interface(
/* input */.clk (clk_100m ),
/* input */.sclk (clk_100m_75deg ),
/* input */.rst_n (rst_n ),
/* input [15:0] */.din (sdram_dout ),// 从wrfifo来的数据
/* output [15:0] */.dout (sdram_din ),// 给rdfifo的数据
/* output */.dout_vld (sdram_din_vld ),
/* input [23:0] */.brc_address (brc_address ),// bank row col 地址 2+13+9
/* input */.rd_req (rd_req ),// 读请求
/* input */.wr_req (wr_req ),// 写请求
/* output */.busy (busy ),// 接口busy
/* output */.ack (ack ),// 接口回应
/* output */.sdram_clk (sdram_clk ),
/* output */.sdram_cke (sdram_cke ),
/* output */.sdram_cs_n (sdram_cs_n ),
/* output [1:0] */.sdram_bank (sdram_bank ),
/* output [12:0] */.sdram_address (sdram_address ),
/* output */.sdram_ras_n (sdram_ras_n ),
/* output */.sdram_cas_n (sdram_cas_n ),
/* output */.sdram_we_n (sdram_we_n ),
/* output [1:0] */.sdram_dqm (sdram_dqm ),
/* input [15:0] */.sdram_dq_in (sdram_dq_in ),
/* output [15:0] */.sdram_dq_out (sdram_dq_out ),
/* output */.sdram_dq_out_en (sdram_dq_out_en)
);
endmodule
3.其他模块
串口发送模块
串口接收模块
按键消抖模块
三、仿真验证
这里我直接用signal tap没啥仿真
四、上板验证
我的突发长度是8
因为串口是8位数据,sdram是16位数据
五、总结
这个sdram我又是写了一周,一直看手册,一直漏条件,一直修改,本来想在sdram控制模块加个读写仲裁的,发现不行,嫌麻烦的我就直接上手按键了,我的fpga学习周期也快结束了,期望能在结束前写完全部的项目。