FPGA-DDR-SDRAM/RTL/ddr_sdram_ctrl.sv

535 lines
20 KiB
Systemverilog
Raw Normal View History

2022-03-30 02:03:32 +08:00
//--------------------------------------------------------------------------------------------------------
// Module : ddr_sdram_ctrl
// Type : synthesizable, IP's top
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// Function: DDR-SDRAM (DDR1) controller
// with AXI4 interface
//--------------------------------------------------------------------------------------------------------
2021-01-27 18:06:35 +08:00
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
2022-03-30 02:03:32 +08:00
input wire rstn_async,
2021-01-27 18:06:35 +08:00
input wire clk, // driving clock, typically 300~532MHz
2022-03-30 02:03:32 +08:00
// user interface ( AXI4 )
2021-01-27 18:06:35 +08:00
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;
2022-03-30 02:03:32 +08:00
reg clk2 = '0;
reg init_done = '0;
reg [2:0] ref_idle = 3'd1, ref_real = '0;
reg [9:0] ref_cnt = '0;
reg [7:0] cnt = '0;
enum logic [3:0] {RESET, IDLE, CLEARDLL, REFRESH, WPRE, WRITE, WRESP, WWAIT, RPRE, READ, RRESP, RWAIT} stat = RESET;
2021-01-27 18:06:35 +08:00
2022-03-30 02:03:32 +08:00
reg [7:0] burst_len = '0;
2021-01-27 18:06:35 +08:00
wire burst_last = cnt==burst_len;
2022-03-30 02:03:32 +08:00
reg [COL_BITS-2:0] col_addr = '0;
2021-01-27 18:06:35 +08:00
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;
2022-03-30 02:03:32 +08:00
reg output_enable='0, output_enable_d1='0, output_enable_d2='0;
reg o_v_a = '0;
reg [(4<<DQ_LEVEL)-1:0] o_dh_a = '0;
reg [(4<<DQ_LEVEL)-1:0] o_dl_a = '0;
reg o_v_b = '0;
reg [(4<<DQ_LEVEL)-1:0] o_dh_b = '0;
reg o_dqs_c = '0;
reg [(4<<DQ_LEVEL)-1:0] o_d_c = '0;
reg [(4<<DQ_LEVEL)-1:0] o_d_d = '0;
reg i_v_a = '0;
reg i_l_a = '0;
reg i_v_b = '0;
reg i_l_b = '0;
reg i_v_c = '0;
reg i_l_c = '0;
reg i_dqs_c = '0;
reg [(4<<DQ_LEVEL)-1:0] i_d_c = '0;
reg i_v_d = '0;
reg i_l_d = '0;
reg [(8<<DQ_LEVEL)-1:0] i_d_d = '0;
reg i_v_e = '0;
reg i_l_e = '0;
reg [(8<<DQ_LEVEL)-1:0] i_d_e = '0;
2021-01-27 18:06:35 +08:00
// -------------------------------------------------------------------------------------
// constants defination and assignment
// -------------------------------------------------------------------------------------
2022-03-30 02:03:32 +08:00
localparam [ROW_BITS-1:0] DDR_A_DEFAULT = (ROW_BITS)'('b0100_0000_0000);
localparam [ROW_BITS-1:0] DDR_A_MR0 = (ROW_BITS)'('b0001_0010_1001);
localparam [ROW_BITS-1:0] DDR_A_MR_CLEAR_DLL = (ROW_BITS)'('b0000_0010_1001);
initial ddr_cs_n = 1'b1;
initial ddr_ras_n = 1'b1;
initial ddr_cas_n = 1'b1;
initial ddr_we_n = 1'b1;
initial ddr_ba = '0;
initial ddr_a = DDR_A_DEFAULT;
initial {aresetn, aclk} = '0;
// -------------------------------------------------------------------------------------
// generate reset sync with clk
// -------------------------------------------------------------------------------------
reg rstn_clk = '0;
reg [1:0] rstn_clk_l = '0;
always @ (posedge clk or negedge rstn_async)
if(~rstn_async)
{rstn_clk, rstn_clk_l} <= '0;
else
{rstn_clk, rstn_clk_l} <= {rstn_clk_l, 1'b1};
// -------------------------------------------------------------------------------------
// generate reset sync with aclk
// -------------------------------------------------------------------------------------
reg rstn_aclk = '0;
reg [1:0] rstn_aclk_l = '0;
always @ (posedge aclk or negedge rstn_async)
if(~rstn_async)
{rstn_aclk, rstn_aclk_l} <= '0;
else
{rstn_aclk, rstn_aclk_l} <= {rstn_aclk_l, 1'b1};
2021-01-27 18:06:35 +08:00
// -------------------------------------------------------------------------------------
// generate clocks
// -------------------------------------------------------------------------------------
2022-03-30 02:03:32 +08:00
always @ (posedge clk or negedge rstn_clk)
if(~rstn_clk)
2021-01-27 18:06:35 +08:00
{aclk,clk2} <= 2'b00;
else
{aclk,clk2} <= {aclk,clk2} + 2'b01;
// -------------------------------------------------------------------------------------
// generate user reset
// -------------------------------------------------------------------------------------
2022-03-30 02:03:32 +08:00
always @ (posedge aclk or negedge rstn_aclk)
if(~rstn_aclk)
2021-01-27 18:06:35 +08:00
aresetn <= 1'b0;
else
aresetn <= init_done;
// -------------------------------------------------------------------------------------
// refresh wptr self increasement
// -------------------------------------------------------------------------------------
2022-03-30 02:03:32 +08:00
always @ (posedge aclk or negedge rstn_aclk)
if(~rstn_aclk) begin
2021-01-27 18:06:35 +08:00
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
// -------------------------------------------------------------------------------------
2022-03-30 02:03:32 +08:00
always @ (posedge aclk or negedge rstn_aclk)
if(~rstn_aclk) begin
2021-01-27 18:06:35 +08:00
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;
2022-03-30 02:03:32 +08:00
{ddr_ba, ddr_a, col_addr} <= awaddr[BA_BITS+ROW_BITS+COL_BITS+DQ_LEVEL-2:DQ_LEVEL];
2021-01-27 18:06:35 +08:00
burst_len <= awlen;
stat <= WPRE;
end else if(arvalid & read_accessible) begin
ddr_ras_n <= 1'b0;
2022-03-30 02:03:32 +08:00
{ddr_ba, ddr_a, col_addr} <= araddr[BA_BITS+ROW_BITS+COL_BITS+DQ_LEVEL-2:DQ_LEVEL];
2021-01-27 18:06:35 +08:00
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)
// -------------------------------------------------------------------------------------
2022-03-30 02:03:32 +08:00
always @ (posedge clk2)
2021-01-27 18:06:35 +08:00
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
// -------------------------------------------------------------------------------------
// 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
2022-03-30 02:03:32 +08:00
localparam AWIDTH = 10;
localparam DWIDTH = 1 + (8<<DQ_LEVEL);
reg [AWIDTH-1:0] wpt = '0, rpt = '0;
reg dvalid = '0, valid = '0;
reg [DWIDTH-1:0] datareg = '0;
wire rreq;
reg [DWIDTH-1:0] fifo_rdata;
wire emptyn = rpt != wpt;
wire itready = rpt != (wpt + (AWIDTH)'(1));
assign rvalid = valid | dvalid;
assign rreq = emptyn & ( rready | ~rvalid );
assign {rlast, rdata} = dvalid ? fifo_rdata : datareg;
always @ (posedge aclk or negedge aresetn)
if(~aresetn)
wpt <= 0;
else if(i_v_e & itready)
wpt <= wpt + (AWIDTH)'(1);
always @ (posedge aclk or negedge aresetn)
if(~aresetn)
rpt <= 0;
else if(rreq & emptyn)
rpt <= rpt + (AWIDTH)'(1);
always @ (posedge aclk or negedge aresetn)
if(~aresetn) begin
dvalid <= 1'b0;
valid <= 1'b0;
datareg <= 0;
end else begin
dvalid <= rreq;
if(dvalid)
datareg <= fifo_rdata;
if(rready)
valid <= 1'b0;
else if(dvalid)
valid <= 1'b1;
end
reg [DWIDTH-1:0] mem [(1<<AWIDTH)];
always @ (posedge aclk)
if(i_v_e)
mem[wpt] <= {i_l_e, i_d_e};
always @ (posedge aclk)
fifo_rdata <= mem[rpt];
2021-01-27 18:06:35 +08:00
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