{ "task_id": "sdram_controller", "description": "SDRAM Controller with FSM, bank management, refresh, and command sequencing", "header": "module sdram_controller (\n input clk,\n input rst_n,\n input [23:0] addr,\n input [15:0] data_in,\n input we,\n input cas,\n input ras,\n input cs_n,\n inputcke,\n input [1:0] dqm,\n output reg [15:0] data_out,\n output reg [12:0] sdram_addr,\n output reg [1:0] ba,\n output reg cs_n_out,\n output reg ras_n_out,\n output reg cas_n_out,\n output reg we_n_out,\n output reg cke_out,\n output reg [1:0] dqm_out,\n output reg busy,\n output reg refresh_req\n);", "module_code": "module sdram_controller (\n input clk,\n input rst_n,\n input [23:0] addr,\n input [15:0] data_in,\n input we,\n input cas,\n input ras,\n input cs_n,\n input cke,\n input [1:0] dqm,\n output reg [15:0] data_out,\n output reg [12:0] sdram_addr,\n output reg [1:0] ba,\n output reg cs_n_out,\n output reg ras_n_out,\n output reg cas_n_out,\n output reg we_n_out,\n output reg cke_out,\n output reg [1:0] dqm_out,\n output reg busy,\n output reg refresh_req\n);\n\n // SDRAM Commands (RAS, CAS, WE combinations)\n localparam CMD_NOP = 3'b111,\n CMD_ACTIVE = 3'b011,\n CMD_READ = 3'b101,\n CMD_WRITE = 3'b100,\n CMD_PRECHARGE = 3'b010,\n CMD_REFRESH = 3'b001,\n CMD_LOAD_MODE = 3'b000,\n CMD_BURST_STOP = 3'b110;\n\n // FSM States\n localparam IDLE = 6'd0,\n INIT = 6'd1,\n INIT_PRECHARGE = 6'd2,\n INIT_LOAD_MODE = 6'd3,\n INIT_REFRESH1 = 6'd4,\n INIT_REFRESH2 = 6'd5,\n ACTIVE = 6'd6,\n READ = 6'd7,\n READ_DATA = 6'd8,\n WRITE = 6'd9,\n WRITE_DATA = 6'd10,\n PRECHARGE = 6'd11,\n REFRESH = 6'd12,\n AUTO_REFRESH = 6'd13,\n LOAD_MODE = 6'd14,\n REFRESH_WAIT = 6'd15,\n BURST_STOP = 6'd16,\n POWER_DOWN = 6'd17,\n SELF_REFRESH = 6'd18,\n ERROR = 6'd19;\n\n // Bank states\n reg [3:0] bank_state;\n reg [3:0] bank_open;\n reg [12:0] row_addr [0:3];\n reg [12:0] col_addr;\n reg [1:0] current_bank;\n\n // Timing counters\n reg [4:0] tRC_count;\n reg [3:0] tRCD_count;\n reg [3:0] tRP_count;\n reg [3:0] tWR_count;\n reg [5:0] tRAS_count;\n reg [9:0] refresh_counter;\n reg [3:0] init_count;\n\n // Command generation\n reg [2:0] sdram_cmd;\n reg [12:0] mode_reg;\n\n // FSM state\n reg [5:0] state, next_state;\n reg [5:0] prev_state;\n reg [15:0] data_buf;\n\n // Command assignments\n assign {cs_n_out, ras_n_out, cas_n_out, we_n_out} = sdram_cmd;\n\n // Mode register: BL=2, BT=0, CAS=2, OPMODE=00, AM[2:0]=000\n localparam MODE_REG_VAL = 13'b000_010_0_00_010;\n\n // Timing parameters (clock cycles)\n localparam tRC = 5'd7; // RAS to RAS\n localparam tRCD = 4'd2; // RAS to CAS delay\n localparam tRP = 4'd2; // Precharge to RAS\n localparam tWR = 4'd1; // Write recovery\n localparam tRAS = 5'd5; // RAS active time\n localparam tRFC = 5'd9; // Refresh cycle\n localparam tREFI = 10'd1560; // Refresh interval (1560ns @ 100MHz = 156 cycles)\n\n // FSM: Command generation\n always @(*) begin\n sdram_cmd = CMD_NOP;\n sdram_addr = 13'b0;\n ba = 2'b00;\n cke_out = 1'b1;\n dqm_out = 2'b00;\n\n case (state)\n IDLE: begin\n sdram_cmd = CMD_NOP;\n end\n\n INIT: begin\n sdram_cmd = CMD_NOP;\n end\n\n INIT_PRECHARGE: begin\n sdram_cmd = CMD_PRECHARGE;\n sdram_addr[10] = 1'b1; // All banks\n end\n\n INIT_LOAD_MODE: begin\n sdram_cmd = CMD_LOAD_MODE;\n sdram_addr = MODE_REG_VAL;\n end\n\n INIT_REFRESH1, INIT_REFRESH2, REFRESH, AUTO_REFRESH: begin\n sdram_cmd = CMD_REFRESH;\n end\n\n ACTIVE: begin\n sdram_cmd = CMD_ACTIVE;\n sdram_addr = addr[23:11]; // Row address\n ba = addr[24:23]; // Bank address\n end\n\n READ: begin\n sdram_cmd = CMD_READ;\n sdram_addr = {3'b000, addr[10], addr[9:0]}; // Column with auto-precharge\n ba = addr[24:23];\n dqm_out = 2'b00;\n end\n\n READ_DATA: begin\n sdram_cmd = CMD_NOP;\n dqm_out = 2'b00;\n end\n\n WRITE: begin\n sdram_cmd = CMD_WRITE;\n sdram_addr = {3'b000, addr[10], addr[9:0]};\n ba = addr[24:23];\n dqm_out = 2'b00;\n end\n\n WRITE_DATA: begin\n sdram_cmd = CMD_NOP;\n end\n\n PRECHARGE: begin\n sdram_cmd = CMD_PRECHARGE;\n sdram_addr[10] = 1'b0; // Single bank\n ba = current_bank;\n end\n\n LOAD_MODE: begin\n sdram_cmd = CMD_LOAD_MODE;\n sdram_addr = mode_reg;\n end\n\n BURST_STOP: begin\n sdram_cmd = CMD_BURST_STOP;\n end\n\n POWER_DOWN: begin\n cke_out = 1'b0;\n sdram_cmd = CMD_NOP;\n end\n\n SELF_REFRESH: begin\n cke_out = 1'b0;\n sdram_cmd = CMD_REFRESH;\n end\n\n ERROR: begin\n sdram_cmd = CMD_NOP;\n end\n\n default: sdram_cmd = CMD_NOP;\n endcase\n end\n\n // FSM: State transition\n always @(*) begin\n next_state = state;\n case (state)\n IDLE: begin\n if (!rst_n) begin\n next_state = INIT;\n end else if (refresh_req && (bank_state == 4'b0000)) begin\n next_state = REFRESH;\n end else if (we && !busy && !cs_n) begin\n next_state = ACTIVE;\n end else if (!we && !busy && !cs_n) begin\n next_state = ACTIVE;\n end else if (cke == 1'b0) begin\n next_state = POWER_DOWN;\n end\n end\n\n INIT: begin\n next_state = INIT_PRECHARGE;\n end\n\n INIT_PRECHARGE: begin\n if (tRP_count >= tRP) begin\n next_state = INIT_LOAD_MODE;\n end\n end\n\n INIT_LOAD_MODE: begin\n if (init_count >= 4'd1) begin\n next_state = INIT_REFRESH1;\n end\n end\n\n INIT_REFRESH1: begin\n if (tRFC_count >= tRFC) begin\n next_state = INIT_REFRESH2;\n end\n end\n\n INIT_REFRESH2: begin\n if (tRFC_count >= tRFC) begin\n if (init_count >= 4'd3) begin\n next_state = IDLE;\n end else begin\n next_state = INIT_REFRESH1;\n end\n end\n end\n\n ACTIVE: begin\n if (tRCD_count >= tRCD) begin\n if (we) begin\n next_state = WRITE;\n end else begin\n next_state = READ;\n end\n end\n end\n\n READ: begin\n next_state = READ_DATA;\n end\n\n READ_DATA: begin\n next_state = IDLE;\n end\n\n WRITE: begin\n next_state = WRITE_DATA;\n end\n\n WRITE_DATA: begin\n if (tWR_count >= tWR) begin\n next_state = PRECHARGE;\n end\n end\n\n PRECHARGE: begin\n if (tRP_count >= tRP) begin\n next_state = IDLE;\n end\n end\n\n REFRESH: begin\n if (tRFC_count >= tRFC) begin\n next_state = REFRESH_WAIT;\n end\n end\n\n REFRESH_WAIT: begin\n next_state = IDLE;\n end\n\n AUTO_REFRESH: begin\n if (tRFC_count >= tRFC) begin\n next_state = IDLE;\n end\n end\n\n LOAD_MODE: begin\n next_state = IDLE;\n end\n\n BURST_STOP: begin\n next_state = IDLE;\n end\n\n POWER_DOWN: begin\n if (cke == 1'b1) begin\n next_state = IDLE;\n end\n end\n\n SELF_REFRESH: begin\n if (cke == 1'b1) begin\n next_state = IDLE;\n end\n end\n\n ERROR: begin\n next_state = IDLE;\n end\n\n default: next_state = IDLE;\n endcase\n end\n\n // FSM: Sequential logic\n always @(posedge clk or negedge rst_n) begin\n if (!rst_n) begin\n state <= INIT;\n prev_state <= IDLE;\n busy <= 1'b1;\n refresh_req <= 1'b0;\n refresh_counter <= 10'd0;\n init_count <= 4'd0;\n tRC_count <= 5'd0;\n tRCD_count <= 4'd0;\n tRP_count <= 4'd0;\n tWR_count <= 4'd0;\n tRAS_count <= 5'd0;\n tRFC_count <= 5'd0;\n bank_state <= 4'b0000;\n bank_open <= 4'b0000;\n current_bank <= 2'b00;\n data_out <= 16'd0;\n data_buf <= 16'd0;\n mode_reg <= MODE_REG_VAL;\n end else begin\n prev_state <= state;\n state <= next_state;\n\n // Refresh counter\n if (state == IDLE) begin\n if (refresh_counter >= tREFI) begin\n refresh_req <= 1'b1;\n refresh_counter <= 10'd0;\n end else begin\n refresh_counter <= refresh_counter + 1'b1;\n end\n end else if (state == REFRESH || state == AUTO_REFRESH) begin\n refresh_req <= 1'b0;\n end\n\n // Initialization counter\n if (state == INIT_LOAD_MODE) begin\n init_count <= init_count + 1'b1;\n end\n\n // Timing counters\n case (state)\n INIT_PRECHARGE: begin\n if (tRP_count < tRP) begin\n tRP_count <= tRP_count + 1'b1;\n end\n end\n\n INIT_REFRESH1, INIT_REFRESH2, REFRESH, AUTO_REFRESH: begin\n if (tRFC_count < tRFC) begin\n tRFC_count <= tRFC_count + 1'b1;\n end else begin\n tRFC_count <= 5'd0;\n end\n end\n\n ACTIVE: begin\n if (tRCD_count < tRCD) begin\n tRCD_count <= tRCD_count + 1'b1;\n end\n tRAS_count <= 5'd0;\n end\n\n READ, READ_DATA: begin\n tRCD_count <= 4'd0;\n end\n\n WRITE, WRITE_DATA: begin\n tRCD_count <= 4'd0;\n if (tWR_count < tWR) begin\n tWR_count <= tWR_count + 1'b1;\n end\n end\n\n PRECHARGE: begin\n tWR_count <= 4'd0;\n if (tRP_count < tRP) begin\n tRP_count <= tRP_count + 1'b1;\n end else begin\n tRP_count <= 4'd0;\n end\n end\n\n default: begin\n tRCD_count <= 4'd0;\n tRP_count <= 4'd0;\n tWR_count <= 4'd0;\n tRFC_count <= 5'd0;\n end\n endcase\n\n // Bank state management\n case (state)\n ACTIVE: begin\n current_bank <= addr[24:23];\n bank_state[addr[24:23]] <= 1'b1;\n bank_open[addr[24:23]] <= 1'b1;\n row_addr[addr[24:23]] <= addr[23:11];\n busy <= 1'b1;\n end\n\n PRECHARGE: begin\n bank_state[current_bank] <= 1'b0;\n bank_open[current_bank] <= 1'b0;\n end\n\n READ_DATA, WRITE_DATA: begin\n if (state == READ_DATA) begin\n data_out <= data_buf;\n end\n busy <= 1'b0;\n end\n\n IDLE: begin\n busy <= 1'b0;\n end\n\n INIT, INIT_PRECHARGE, INIT_LOAD_MODE, INIT_REFRESH1, INIT_REFRESH2: begin\n busy <= 1'b1;\n end\n\n REFRESH, AUTO_REFRESH: begin\n busy <= 1'b1;\n end\n endcase\n end\n end\n\nendmodule", "testbench": "`timescale 1ns / 1ps\n\nmodule tb_sdram_controller;\n reg clk;\n reg rst_n;\n reg [23:0] addr;\n reg [15:0] data_in;\n reg we;\n reg cas;\n reg ras;\n reg cs_n;\n reg cke;\n reg [1:0] dqm;\n wire [15:0] data_out;\n wire [12:0] sdram_addr;\n wire [1:0] ba;\n wire cs_n_out;\n wire ras_n_out;\n wire cas_n_out;\n wire we_n_out;\n wire cke_out;\n wire [1:0] dqm_out;\n wire busy;\n wire refresh_req;\n\n sdram_controller dut (\n .clk(clk),\n .rst_n(rst_n),\n .addr(addr),\n .data_in(data_in),\n .we(we),\n .cas(cas),\n .ras(ras),\n .cs_n(cs_n),\n .cke(cke),\n .dqm(dqm),\n .data_out(data_out),\n .sdram_addr(sdram_addr),\n .ba(ba),\n .cs_n_out(cs_n_out),\n .ras_n_out(ras_n_out),\n .cas_n_out(cas_n_out),\n .we_n_out(we_n_out),\n .cke_out(cke_out),\n .dqm_out(dqm_out),\n .busy(busy),\n .refresh_req(refresh_req)\n );\n\n always #5 clk = ~clk;\n\n initial begin\n clk = 0;\n rst_n = 0;\n addr = 24'h000000;\n data_in = 16'h0000;\n we = 1;\n cas = 1;\n ras = 1;\n cs_n = 1;\n cke = 0;\n dqm = 2'b00;\n\n #50 rst_n = 1;\n cke = 1;\n #100;\n\n wait(busy == 0);\n\n @(posedge clk);\n addr = 24'h100000;\n data_in = 16'hDEAD;\n we = 0;\n cas = 0;\n ras = 1;\n cs_n = 0;\n #10;\n we = 1;\n cas = 1;\n ras = 1;\n cs_n = 1;\n\n wait(busy == 0);\n #100;\n\n @(posedge clk);\n addr = 24'h100000;\n we = 1;\n cas = 0;\n ras = 1;\n cs_n = 0;\n #10;\n we = 1;\n cas = 1;\n ras = 1;\n cs_n = 1;\n\n wait(busy == 0);\n #500;\n\n @(posedge clk);\n addr = 24'h200000;\n data_in = 16'hBEEF;\n we = 0;\n cas = 0;\n ras = 1;\n cs_n = 0;\n #10;\n we = 1;\n cas = 1;\n ras = 1;\n cs_n = 1;\n\n wait(busy == 0);\n #1000;\n\n $finish;\n end\n\nendmodule" }