commit 69e1beb55107e6a3733c77b3f40ea8b00bc01720
parent eb5ec9b9fcacde2638a58fc8f094a10abae850c2
Author: Brian Swetland <swetland@frotz.net>
Date: Thu, 30 Jan 2020 22:31:39 -0800
sdram: more work in progress
- stop auto-precharging after read/write
- track which rows of which banks are open
- auto-precharge-all before refresh
- read/write immediately if bank is open to correct row
- start wiring up burst read/write ops
- spent a lot of time trying to simplify to meet timing
Diffstat:
5 files changed, 311 insertions(+), 147 deletions(-)
diff --git a/hdl/sdram/sdram.sv b/hdl/sdram/sdram.sv
@@ -4,18 +4,18 @@
`default_nettype none
// tRCD (RAS# to CAS# Delay Time)
-// Min clocks betwen ACTIVATE of a bank and a READ or WRITE of that bank
+// Min clocks betwen ACTIVE of a bank and a READ or WRITE of that bank
//
// tRC (RAS# Cycle Time)
-// Min clocks between ACTIVATE of one row in a bank and ACTIVATE of a
+// Min clocks between ACTIVE of one row in a bank and ACTIVE of a
// different row in the /same/ bank. PRECHARGE must happen in between.
//
// tRRD (Row Activate to Row Activate Delay)
-// Min time between ACTIVATE of a row in one bank and the ACTIVATE of
+// Min time between ACTIVE of a row in one bank and the ACTIVE of
// a row in a /different/ bank.
//
// tRP (Precharge to Refresh/Activate)
-// Min time between PREFRESH of a bank and REFRESH or ACTIVATE of it
+// Min time between PREFRESH of a bank and REFRESH or ACTIVE of it
//
// tWR (Write Recovery Time)
// Min clocks between the last word of a write and PRECHARGE of that bank
@@ -51,14 +51,18 @@ module sdram #(
output wire [AWIDTH-1:0]pin_addr,
input wire [XWIDTH-1:0]rd_addr,
- input wire rd_ready,
+ input wire [3:0]rd_len,
+ input wire rd_req,
+ output reg rd_ack = 0,
+
output reg [DWIDTH-1:0]rd_data,
- output reg rd_valid = 0,
+ output reg rd_rdy = 0,
input wire [XWIDTH-1:0]wr_addr,
input wire [DWIDTH-1:0]wr_data,
- input wire wr_valid,
- output reg wr_ready = 0
+ input wire [3:0]wr_len,
+ input wire wr_req,
+ output reg wr_ack = 0
);
// sdram addr is wide enough for row + bank
@@ -67,184 +71,295 @@ localparam AWIDTH = (ROWBITS + BANKBITS);
// full addr is rowbits + bankbits + colbits wide
localparam XWIDTH = (ROWBITS + BANKBITS + COLBITS);
-wire [COLBITS-1:0]wr_col;
+localparam BANKCOUNT = (1 << BANKBITS);
+
+integer i; // used by various for loops
+
+// split input address into bank, row, col
wire [BANKBITS-1:0]wr_bank;
wire [ROWBITS-1:0]wr_row;
+wire [COLBITS-1:0]wr_col;
assign {wr_row, wr_bank, wr_col} = wr_addr;
-wire [COLBITS-1:0]rd_col;
+// split input address into bank, row, col
wire [BANKBITS-1:0]rd_bank;
wire [ROWBITS-1:0]rd_row;
+wire [COLBITS-1:0]rd_col;
assign {rd_row, rd_bank, rd_col} = rd_addr;
-// high bits for read/write command addresses
-localparam x1_col = { ROWBITS - COLBITS { 1'b1 } };
-localparam x0_col = { ROWBITS - COLBITS { 1'b0 } };
+// sdram io address management
+reg [XWIDTH-1:0]io_addr = 0;
+reg [XWIDTH-1:0]io_addr_next;
+
+wire [COLBITS-1:0]io_col;
+wire [BANKBITS-1:0]io_bank;
+wire [ROWBITS-1:0]io_row;
+assign {io_row, io_bank, io_col} = io_addr;
+wire [COLBITS-1:0]io_col_add1 = io_col + {{COLBITS-1{1'b0}},1'b1};
reg [DWIDTH-1:0]rd_data_next;
-reg rd_valid_next;
-reg wr_ready_next;
+reg rd_ack_next;
+reg rd_rdy_next;
+reg wr_ack_next;
-reg ras_n = 1;
-reg cas_n = 1;
-reg we_n = 1;
-reg [AWIDTH-1:0]addr = 0;
-reg [DWIDTH-1:0]data_o = 0;
+// signals to sdram_glue module
+wire ras_n;
+wire cas_n;
+wire we_n;
+wire [AWIDTH-1:0]addr;
wire [DWIDTH-1:0]data_i;
+reg [DWIDTH-1:0]data_o = 0;
reg data_oe = 0;
+reg [DWIDTH-1:0]data_o_next;
+reg data_oe_next;
+
+reg [2:0]cmd = 3'b111;
+reg [2:0]cmd_next;
+
// next refresh down counter
-reg [11:0]refresh = 0;
-reg [11:0]refresh_next;
-wire [11:0]refresh_sub1;
-wire refresh_now;
-assign { refresh_now, refresh_sub1 } = { 1'b0, refresh } - 13'd1;
+reg [15:0]refresh = T_PWR_UP;
+reg [15:0]refresh_next;
+wire [15:0]refresh_sub1 = refresh - 16'd1;
+wire refresh_done = refresh[15];
// general purpose down counter
-reg [15:0]count = 0;
-reg [15:0]count_next;
-wire [15:0]count_sub1;
-wire count_done;
-assign { count_done, count_sub1 } = { 1'b0, count } - 17'd1;
+reg [4:0]count = 0;
+reg [4:0]count_next;
+wire [4:0]count_sub1 = count - 5'd1;
+wire count_done = count[4];
-reg [2:0]cmd_next;
-reg [AWIDTH-1:0]addr_next;
-reg [DWIDTH-1:0]data_o_next;
-reg data_oe_next;
+// sdram bank state
+reg bank_active[0:BANKCOUNT-1];
+reg bank_active_next[0:BANKCOUNT-1];
+reg [ROWBITS-1:0]bank_row[0:BANKCOUNT-1];
+reg [ROWBITS-1:0]bank_row_next[0:BANKCOUNT-1];
+// state machine state
localparam START = 4'd0;
-localparam INIT0 = 4'd1;
-localparam INIT1 = 4'd2;
-localparam INIT2 = 4'd3;
-localparam INIT3 = 4'd4;
-localparam INIT4 = 4'd5;
-localparam IDLE = 4'd6;
-localparam WACTIVE = 4'd7;
+localparam IDLE = 4'd1;
+localparam INIT0 = 4'd2;
+localparam INIT1 = 4'd3;
+localparam INIT2 = 4'd4;
+localparam REFRESH = 4'd5;
+localparam ACTIVE = 4'd6;
+localparam READ = 4'd7;
localparam WRITE = 4'd8;
-localparam RACTIVE = 4'd9;
-localparam READ = 4'd10;
-localparam RCAP = 4'd11;
reg [3:0]state = START;
reg [3:0]state_next;
+// sdram commands
localparam CMD_SET_MODE = 3'b000; // A0-9 mode, A10+ SBZ
localparam CMD_REFRESH = 3'b001;
-localparam CMD_PRECHARGE = 3'b010; // A10=all, BA*=bankno
-localparam CMD_ACTIVATE = 3'b011; // BA*=bankno
-localparam CMD_WRITE = 3'b100;
-localparam CMD_READ = 3'b101;
+localparam CMD_PRECHARGE = 3'b010; // BA*=bankno, A10=ALL
+localparam CMD_ACTIVE = 3'b011; // BA*=bankno, A*=ROW
+localparam CMD_WRITE = 3'b100; // BA*=bankno, A10=AP, COLADDR
+localparam CMD_READ = 3'b101; // BA*=bankno, A10=AP, COLADDR
localparam CMD_STOP = 3'b110;
localparam CMD_NOP = 3'b111;
+// TODO CL2 vs CL3 configurability here and elsewhere
+
+reg [3:0]rd_pipe_rdy = 0;
+reg [3:0]rd_pipe_bsy = 0;
+reg [3:0]rd_pipe_rdy_next;
+reg [3:0]rd_pipe_bsy_next;
+
+reg [3:0]burst = 0;
+reg [3:0]burst_next;
+wire [3:0]burst_sub1;
+wire burst_done;
+assign { burst_done, burst_sub1 } = { 1'b0, burst } - 5'd1;
+
+reg io_sel_a10 = 0;
+reg io_sel_a10_next;
+reg io_sel_row = 0;
+reg io_sel_row_next;
+
always_comb begin
state_next = state;
count_next = count_done ? count : count_sub1;
- refresh_next = refresh_now ? refresh : refresh_sub1;
+ refresh_next = refresh_done ? refresh : refresh_sub1;
cmd_next = CMD_NOP;
- addr_next = addr;
data_o_next = data_o;
- data_oe_next = data_oe;
- wr_ready_next = 0;
- rd_valid_next = 0;
+ data_oe_next = 0;
+ wr_ack_next = 0;
+ rd_rdy_next = 0;
+ rd_ack_next = 0;
rd_data_next = rd_data;
+ burst_next = burst;
+ io_addr_next = io_addr;
+ io_sel_a10_next = io_sel_a10;
+ io_sel_row_next = io_sel_row;
+
+ for (i = 0; i < BANKCOUNT; i++) begin
+ bank_active_next[i] = bank_active[i];
+ bank_row_next[i] = bank_row[i];
+ end
+ // read pipe regs track inbound read data (rdy)
+ // and hold off writes (bsy) to avoid bus conflict
+ rd_pipe_rdy_next = { 1'b0, rd_pipe_rdy[3:1] };
+ rd_pipe_bsy_next = { 1'b0, rd_pipe_bsy[3:1] };
+
+ if (rd_pipe_rdy[0]) begin
+ rd_rdy_next = 1;
+ rd_data_next = data_i;
+ end
+
+ if (count_done) // state can only advance if counter is 0
case (state)
START: begin
+ refresh_next = T_PWR_UP;
state_next = INIT0;
- count_next = T_PWR_UP - 1;
end
- IDLE: if (count_done) begin
+ IDLE: begin
data_oe_next = 0;
- if (refresh_now) begin
- cmd_next = CMD_REFRESH;
- refresh_next = T_RI - 1;
- count_next = T_RC - 1;
- end else if (rd_ready) begin
- state_next = RACTIVE;
- count_next = T_RCD - 1;
- cmd_next = CMD_ACTIVATE;
- addr_next = { rd_bank, rd_row };
- end else if (wr_valid) begin
- state_next = WACTIVE;
- count_next = T_RCD - 1;
- cmd_next = CMD_ACTIVATE;
- addr_next = { wr_bank, wr_row };
- data_oe_next = 1;
+ if (refresh_done) begin
+ // refresh counter has expired, precharge all and refresh
+ state_next = REFRESH;
+ cmd_next = CMD_PRECHARGE;
+ io_sel_row_next = 0;
+ io_sel_a10_next = 1; // ALL BANKS
+ count_next = T_RP - 2;
+ end else if (rd_req && !rd_ack) begin
+ io_addr_next = rd_addr;
+ if (!bank_active[rd_bank] || (bank_row[rd_bank] != rd_row)) begin
+ state_next = ACTIVE;
+ cmd_next = CMD_PRECHARGE;
+ io_sel_row_next = 0; // column addr
+ io_sel_a10_next = 0; // one bank only
+ count_next = T_RP - 2;
+ end else begin
+ cmd_next = CMD_READ;
+ io_sel_row_next = 0; // column addr
+ io_sel_a10_next = 0; // no auto precharge
+ rd_pipe_rdy_next = { 1'b1, rd_pipe_rdy[3:1] };
+ rd_pipe_bsy_next = 4'b1111;
+ rd_ack_next = 1;
+ if (rd_len != 4'd0) begin
+ state_next = READ;
+ burst_next = rd_len;
+ end
+ end
+ end else if (wr_req && !rd_pipe_bsy[0] && !wr_ack) begin
+ io_addr_next = wr_addr;
+ if (!bank_active[wr_bank] || (bank_row[wr_bank] != wr_row)) begin
+ state_next = ACTIVE;
+ // precharge one bank (a10=0)
+ cmd_next = CMD_PRECHARGE;
+ io_sel_row_next = 0; // column addr
+ io_sel_a10_next = 0; // one bank only
+ count_next = T_RP - 2;
+ end else begin
+ cmd_next = CMD_WRITE;
+ io_sel_row_next = 0; // column addr
+ io_sel_a10_next = 0; // no auto precharge
+ data_o_next = wr_data;
+ data_oe_next = 1;
+ wr_ack_next = 1; // 1cyc delay eww
+ if (wr_len != 4'd0) begin
+ burst_next = wr_len;
+ state_next = WRITE;
+ end
+ end
end
end
- WACTIVE: if (count_done) begin
+ ACTIVE: begin
state_next = IDLE;
- cmd_next = CMD_WRITE;
- count_next = T_WR + T_RP - 1;
- addr_next = { wr_bank, x1_col, wr_col };
- data_o_next = wr_data;
- wr_ready_next = 1;
+ count_next = T_RCD - 2;
+ cmd_next = CMD_ACTIVE;
+ io_sel_row_next = 1; // row address
+ bank_active_next[io_bank] = 1;
+ bank_row_next[io_bank] = io_row;
end
- RACTIVE: if (count_done) begin
- state_next = READ;
+ READ: begin
+ if (burst_done) begin
+ state_next = IDLE;
+ end else begin
+ burst_next = burst_sub1;
+ end
+ // column addressing pre-selected from initial read
+ io_addr_next[COLBITS-1:0] = io_col_add1;
cmd_next = CMD_READ;
- count_next = T_RCD ;
- addr_next = { rd_bank, x1_col, rd_col };
+ rd_pipe_rdy_next = { 1'b1, rd_pipe_rdy[3:1] };
+ rd_pipe_bsy_next = 4'b1111;
end
- READ: if (count_done) begin
- state_next = IDLE;
- count_next = 2; // ??
- rd_data_next = data_i;
- rd_valid_next = 1;
+ WRITE: begin
+ if (burst_done) begin
+ state_next = IDLE;
+ end else begin
+ burst_next = burst_sub1;
+ end
+ // column addressing pre-selected from initial write
+ io_addr_next[COLBITS-1:0] = io_col_add1;
+ cmd_next = CMD_WRITE;
+ data_oe_next = 1;
end
- INIT0: if (count_done) begin
+ INIT0: if (refresh_done) begin
state_next = INIT1;
count_next = 2;
cmd_next = CMD_PRECHARGE;
- addr_next[10] = 1; // ALL
+ io_sel_row_next = 0; // column addressing
+ io_sel_a10_next = 1; // ALL BANKS
end
- INIT1: if (count_done) begin
+ INIT1: begin
state_next = INIT2;
cmd_next = CMD_SET_MODE;
- count_next = T_MRD - 1;
+ count_next = T_MRD - 2;
// r/w burst off, cas lat 3, sequential addr
- addr_next = { {(AWIDTH - 10){1'b0}}, 10'b0000110000};
+ io_addr_next[XWIDTH-1:COLBITS] = { {(AWIDTH - 10){1'b0}}, 10'b0000110000};
+ io_sel_row_next = 1; // row addressing
end
- INIT2: if (count_done) begin
- state_next = INIT3;
+ INIT2: begin
+ state_next = REFRESH;
cmd_next = CMD_REFRESH;
- count_next = T_RC - 1;
+ count_next = T_RC - 2;
end
- INIT3: if (count_done) begin
- state_next = INIT4;
- cmd_next = CMD_REFRESH;
- count_next = T_RC - 1;
- end
- INIT4: if (count_done) begin
+ REFRESH: begin
state_next = IDLE;
+ cmd_next = CMD_REFRESH;
+ count_next = T_RC - 2;
refresh_next = T_RI - 1;
+
+ // we got here after a precharge all
+ for (i = 0; i < BANKCOUNT; i++)
+ bank_active_next[i] = 0;
end
default: begin
- state_next = START;
+ //state_next = START;
end
endcase
end
-// debug
-reg [2:0]cmd = 3'b111;
-
always_ff @(posedge clk) begin
state <= state_next;
count <= count_next;
refresh <= refresh_next;
- ras_n <= cmd_next[2];
- cas_n <= cmd_next[1];
- we_n <= cmd_next[0];
cmd <= cmd_next;
- addr <= addr_next;
+ io_sel_a10 <= io_sel_a10_next;
+ io_sel_row <= io_sel_row_next;
+ io_addr <= io_addr_next;
data_o <= data_o_next;
data_oe <= data_oe_next;
- wr_ready <= wr_ready_next;
- rd_valid <= rd_valid_next;
+ wr_ack <= wr_ack_next;
+ rd_ack <= rd_ack_next;
+ rd_rdy <= rd_rdy_next;
rd_data <= rd_data_next;
+ for (i = 0; i < BANKCOUNT; i++) begin
+ bank_active[i] <= bank_active_next[i];
+ bank_row[i] <= bank_row_next[i];
+ end
+ rd_pipe_rdy <= rd_pipe_rdy_next;
+ rd_pipe_bsy <= rd_pipe_bsy_next;
end
+assign { ras_n, cas_n, we_n } = cmd;
+
+wire [(ROWBITS-COLBITS)-1:0]io_misc = {{(ROWBITS-COLBITS)-1{1'b0}}, io_sel_a10 } << (10 - COLBITS);
+wire [ROWBITS-1:0]io_low = io_sel_row ? io_row : { io_misc, io_col };
+assign addr = { io_bank, io_low };
`ifdef verilator
assign pin_clk = clk;
diff --git a/hdl/sdram/sdram_glue_ecp5.sv b/hdl/sdram/sdram_glue_ecp5.sv
@@ -41,7 +41,7 @@ generate
for (n = 0; n < DWIDTH; n++) begin
BB iobuf (
.I(data_o[n]),
- .T(~data_oe[n]),
+ .T(~data_oe),
.O(data_i[n]),
.B(pin_data[n])
);
diff --git a/hdl/sdram/testbench.sv b/hdl/sdram/testbench.sv
@@ -31,20 +31,32 @@ reg info_e_next;
reg [19:0]rd_addr = 0;
wire [15:0]rd_data;
-reg rd_ready = 0;
-wire rd_valid;
+reg rd_req = 0;
+reg [3:0]rd_len = 0;
+wire rd_ack;
+wire rd_rdy;
reg [19:0]wr_addr = 0;
reg [15:0]wr_data = 0;
-reg wr_valid = 0;
-wire wr_ready;
+reg wr_req = 0;
+wire wr_ack;
-reg [31:0]count = T_PWR_UP + 32;
-reg [31:0]count_next;
-wire [31:0]count_sub1;
+reg rd_req_next;
+reg wr_req_next;
+reg [3:0]rd_len_next;
+reg [19:0]wr_addr_next;
+reg [19:0]rd_addr_next;
+reg [15:0]wr_data_next;
+
+reg done_next;
+reg error_next;
+
+reg [15:0]count = T_PWR_UP + 32;
+reg [15:0]count_next;
+wire [15:0]count_sub1;
wire count_done;
-assign { count_done, count_sub1 } = { 1'b0, count } - 32'd1;
+assign { count_done, count_sub1 } = { 1'b0, count } - 16'd1;
localparam INIT = 4'd0;
localparam WRITES = 4'd1;
@@ -54,32 +66,49 @@ localparam STOP = 4'd3;
reg [3:0]state = INIT;
reg [3:0]state_next;
-reg rd_ready_next;
-reg wr_valid_next;
-reg [19:0]wr_addr_next;
-reg [19:0]rd_addr_next;
-reg [15:0]wr_data_next;
-reg done_next;
+reg number_next;
+reg number_reset;
+wire [31:0]number;
+
+xorshift32 xs(
+ .clk(clk),
+ .next(number_next),
+ .reset(number_reset),
+ .data(number)
+);
+
+reg [15:0]cycles = 0;
+reg [15:0]cycles_next;
always_comb begin
+ number_reset = 0;
+ number_next = 0;
state_next = state;
count_next = count;
- rd_ready_next = rd_ready;
- wr_valid_next = wr_valid;
+ rd_req_next = rd_req;
+ wr_req_next = wr_req;
wr_addr_next = wr_addr;
rd_addr_next = rd_addr;
+ rd_len_next = rd_len;
wr_data_next = wr_data;
info_next = info;
info_e_next = 0;
done_next = 0;
+ error_next = 0;
+ cycles_next = cycles + 16'd1;
+
+ if (cycles == 16'd5000)
+ error_next = 1;
case (state)
INIT: if (count_done) begin
state_next = WRITES;
- count_next = 32;
- wr_addr_next = 0;
- wr_data_next = 0;
- wr_valid_next = 1;
+ count_next = 32; //1000; //32;
+ wr_addr_next = 20'hF0;
+ //wr_data_next = 0;
+ wr_data_next= number[15:0];
+ number_next = 1;
+ wr_req_next = 1;
info_next = 16'h10FF;
info_e_next = 1;
end else begin
@@ -87,15 +116,18 @@ always_comb begin
end
WRITES: if (count_done) begin
state_next = READS;
- count_next = 32;
- rd_addr_next = 0;
- rd_ready_next = 1;
- wr_valid_next = 0;
+ number_reset = 1;
+ count_next = 32; //1000; //32;
+ rd_addr_next = 20'hF0;
+ rd_req_next = 1;
+ wr_req_next = 0;
info_next = 16'h20EE;
info_e_next = 1;
end else begin
- if (wr_ready) begin
- wr_data_next = wr_data + 1;
+ if (wr_ack) begin
+ //wr_data_next = wr_data + 1;
+ wr_data_next = number[15:0];
+ number_next = 1;
wr_addr_next = wr_addr + 1;
count_next = count_sub1;
end
@@ -103,14 +135,23 @@ always_comb begin
READS: if (count_done) begin
state_next = STOP;
done_next = 1;
- rd_ready_next = 0;
info_next = 16'h20DD;
info_e_next = 1;
+ rd_req_next = 0;
end else begin
- if (rd_valid) begin
+ if (rd_ack) begin
+ rd_req_next = 0;
+ end
+ if (rd_rdy) begin
+ rd_req_next = 1;
rd_addr_next = rd_addr + 1;
count_next = count_sub1;
- info_next = { 8'h40, rd_data[7:0] };
+ if (rd_data == number[15:0])
+ info_next = { 16'h7011 };
+ else
+ info_next = { 16'h40FF };
+ //info_next = { 8'h40, rd_data[7:0] };
+ number_next = 1;
info_e_next = 1;
end
end
@@ -121,15 +162,19 @@ end
always_ff @(posedge clk) begin
state <= state_next;
- rd_ready <= rd_ready_next;
- wr_valid <= wr_valid_next;
+ rd_req <= rd_req_next;
+ wr_req <= wr_req_next;
rd_addr <= rd_addr_next;
wr_addr <= wr_addr_next;
wr_data <= wr_data_next;
+ rd_len <= rd_len_next;
count <= count_next;
- done <= done_next;
info <= info_next;
info_e <= info_e_next;
+
+ cycles <= cycles_next;
+ done <= done_next;
+ error <= error_next;
end
sdram #(
@@ -150,14 +195,17 @@ sdram #(
.pin_data(sdram_data),
`endif
.rd_addr(rd_addr),
+ .rd_len(rd_len),
+ .rd_req(rd_req),
+ .rd_ack(rd_ack),
.rd_data(rd_data),
- .rd_ready(rd_ready),
- .rd_valid(rd_valid),
+ .rd_rdy(rd_rdy),
.wr_addr(wr_addr),
.wr_data(wr_data),
- .wr_valid(wr_valid),
- .wr_ready(wr_ready)
+ .wr_len(0),
+ .wr_req(wr_req),
+ .wr_ack(wr_ack)
);
endmodule
diff --git a/project/colorlight-sdram.def b/project/colorlight-sdram.def
@@ -5,5 +5,6 @@ PROJECT_SRCS += hdl/lattice/ecp5_pll_25_125_250.v
PROJECT_SRCS += hdl/display/display.sv hdl/display/display_timing.sv
PROJECT_SRCS += hdl/sdram/testbench.sv
PROJECT_SRCS += hdl/sdram/sdram.sv hdl/sdram/sdram_glue_ecp5.sv
+PROJECT_SRCS += hdl/xorshift.sv
PROJECT_NEXTPNR_OPTS := --25k --package CABGA381 --speed 6
diff --git a/project/test-sdram.def b/project/test-sdram.def
@@ -2,6 +2,6 @@
PROJECT_TYPE := verilator-sim
PROJECT_SRCS := hdl/sdram/testbench.sv
-PROJECT_SRCS += hdl/sdram/sdram.sv
+PROJECT_SRCS += hdl/sdram/sdram.sv hdl/xorshift.sv
PROJECT_VOPTS := -CFLAGS -DSDRAM