Jump to content

    

Вопросы по SDRAM Controller

Здравствуйте. Разбирался с SDRAM контроллером представленным здесь http://fpga4fun.com/SDRAM2.html (http://fpga4fun.com/files/SDRAM_ctrl.zip) и возникли некоторые вопросы по коду.

 

Код на Verilog с моими комментариями.

 

module sdram_controller(
input             clk,          /* Тактирование */

/* Чтение */
input             RdReq,        /* Запрос на чтение */
output            RdGnt,        /* Запрос на чтение одобрен */
input      [19:0] RdAddr,       /* Адрес. Шина на 20 сигналов */
output reg [15:0] RdData,       /* Прочитанные данные. Шина 16 сигналов */
output            RdDataValid,  /* Достовреность прочитанных данных. Выставляется в 1, если достоверны */

/* Запись */
input             WrReq,        /* Запрос на запись */
output            WrGnt,        /* Запрос на запись одобрен */
input      [19:0] WrAddr,       /* Адрес. Шина на 20 сигналов */
input      [15:0] WrData,       /* Записываемые данные. Шина 16 сигналов */

/* Управление SDRAM */
output            SDRAM_CKE,    /* Clock. Тактирование */
output            SDRAM_WEn,    /* Enable. Включение */
output            SDRAM_RASn,   /* Row address strobe.  */
output            SDRAM_CASn,   /* Column Access strobe */
output reg [10:0] SDRAM_A,      /* Address */
output reg [0:0]  SDRAM_BA,     /* Memory Bank */
output reg [1:0]  SDRAM_DQM = 2'b11, /* Data mask */
inout      [15:0] SDRAM_DQ
);

/* На вход SDRAM.Enable всегда подаём лог. единицу, тем самым включая SDRAM */
assign SDRAM_CKE = 1'b1;

/* Команды управления SDRAM`ом */
localparam [2:0] SDRAM_CMD_LOADMODE  = 3'b000; /* Загрузка регистра управления. Сама команда подаётся по шине адреса(A) */
localparam [2:0] SDRAM_CMD_REFRESH   = 3'b001; /* Автоматическая регенерация. Для поддержания заряда конденсаторов */
localparam [2:0] SDRAM_CMD_PRECHARGE = 3'b010; /* Деактивация. Деактивация открытого ряда в банке */
localparam [2:0] SDRAM_CMD_ACTIVE    = 3'b011; /* Активация. Активировать ряд в отдельном банке. По шине адреса(A) задаётся ряд, по шине банка(BA) банк */
localparam [2:0] SDRAM_CMD_WRITE     = 3'b100; /* Запись. Записывает данные в активный ряд */
localparam [2:0] SDRAM_CMD_READ      = 3'b101; /* Чтение. Читает данные из активного ряда */
localparam [2:0] SDRAM_CMD_NOP       = 3'b111; /* Нет операции. Предохраняет от выполнения нежелательных команд во время ожидания */

/* По-умолчанию в регистре SDRAM_CMD будет висеть команда NOP */
reg [2:0] SDRAM_CMD = SDRAM_CMD_NOP;

/* Каждому управляещему выходу(пину) назначается свой сигнал из регистра SDRAM_CMD.
   При изменении SDRAM_CMD соотествующие выхода будут меняться. */
assign {SDRAM_RASn, SDRAM_CASn, SDRAM_WEn} = SDRAM_CMD;

/* Установка приоритета командам чтение/запись */
wire read_now  =  RdReq;         /* Приоритет у команды чтения */
wire write_now = ~RdReq & WrReq; /* Если нет команды чтения и есть запрос на запись, то разрешаем запись */

/* Регистр статуса. Для организации state-machine */
reg [1:0] state = 0;


/* ?? BEGIN */
reg ReadSelected = 0;
always @(posedge clk) 
  if (state == 2'h0)
    ReadSelected <= read_now;
wire WriteSelected = ~ReadSelected;

wire ReadCycle = (state == 2'h0) ? read_now : ReadSelected;
wire [19:0] Addr = ReadCycle ? RdAddr : WrAddr;
reg  [19:0] AddrR = 0;
always @(posedge clk)
  AddrR <= Addr;
  
/* ?? END */
  

/* Сравниваем строку и банк текущего и предыдущего чтений на предмет изменений */
wire SameRowAndBank = (Addr[19:8] == AddrR[19:8]);
/* Выставляем флаг "Чтение разрешено" если
1) Статус "Ожидание" и есть запрос на чтение
2) Статус "Активирован" и есть выбранная строка блока, запрос на чтение, выборана та же строка что и на предыдущем чтении */
assign RdGnt = (state == 2'h0 &  read_now) | (state == 2'h1 &  ReadSelected & RdReq & SameRowAndBank);
/* Аналогично чтению */
assign WrGnt = (state == 2'h0 & write_now) | (state == 2'h1 & WriteSelected & WrReq & SameRowAndBank);

always @(posedge clk)
case(state)
   /* Статус "Ожидание" */
    2'h0: begin
       /* Есть запрос на чтение/запись */
        if(RdReq | WrReq) begin
            SDRAM_CMD <= SDRAM_CMD_ACTIVE; /* Команда "Активирован" */
            SDRAM_BA  <= Addr[19];         /* Банк памяти */
            SDRAM_A   <= Addr[18:8];       /* Строка */
            SDRAM_DQM <= 2'b11;            /* Выставляем в 11, чтобы не мешало чтению/записи. */
            
            /* Меняем статус на "Активирован" */
            state     <= 2'h1;             
        end
        /* Иначе тупим дальше */
        else
        begin
            SDRAM_CMD <= SDRAM_CMD_NOP;
            SDRAM_BA  <= 0;
            SDRAM_A   <= 0;
            SDRAM_DQM <= 2'b11;
            
            /* Остаёмся в режиме "Ожидание" */
            state     <= 2'h0;
        end
    end
    /* Статус "Активирован" */
    2'h1: begin
        SDRAM_CMD    <= ReadSelected ? SDRAM_CMD_READ : SDRAM_CMD_WRITE; /* Выставляем команду чтения или записи */
        SDRAM_BA     <= AddrR[19];           /* Банк памяти */
        SDRAM_A[9:0] <= {2'b00, AddrR[7:0]}; /* Колонка */
        SDRAM_A[10]  <= 1'b0;                /* Отключаем автоматическую деактивацию */ 
        SDRAM_DQM    <= 2'b00;               /*  */

        /* Если есть запрос на чтение или запись, то остаёмся в режиме "Активирован", иначе переходим в "Деактивация" */
        state        <= (ReadSelected ? RdReq : WrReq) & SameRowAndBank ? 2'h1 : 2'h2; 
    end
    /* Статус "Деактивация" */
    2'h2: begin
        SDRAM_CMD <= SDRAM_CMD_PRECHARGE; /* Команда деактивации */
        SDRAM_BA  <= 0;
        SDRAM_A   <= 11'b100_0000_0000;   /* Деактивация всех банков */
        SDRAM_DQM <= 2'b11;
        state     <= 2'h0;
    end
    /* Статус "Нет операции" */
    2'h3: begin
        SDRAM_CMD <= SDRAM_CMD_NOP;
        SDRAM_BA  <= 0;
        SDRAM_A   <= 0;
        SDRAM_DQM <= 2'b11;
        state     <= 2'h0;
    end
endcase

/* ?? BEGIN */

localparam trl = 4;  // total read latency is the SDRAM CAS-latency (two) plus the SDRAM controller induced latency (two)
reg [trl-1:0] RdDataValidPipe;
always @(posedge clk) 
  RdDataValidPipe <= {RdDataValidPipe[trl-2:0], state == 2'h1 & ReadSelected};
assign RdDataValid = RdDataValidPipe[trl-1];

/* ?? END */

/* Выводим на шину RdData данные от SDRAM по фронту клока */
always @(posedge clk) 
  RdData <= SDRAM_DQ;

/* Сигнал(флаг) - команда записи
   Определяется по состоянию "Активирован" и признаку "запрос на запись" */
reg SDRAM_DQ_OE = 1'b0;
always @(posedge clk)
  SDRAM_DQ_OE <= (state == 2'h1) & WriteSelected;

/* Устранение метастабильности на входе WrData (?) */  
reg [15:0] WrData1 = 0; 
reg [15:0] WrData2 = 0;
always @(posedge clk) 
  WrData1 <= WrData;
always @(posedge clk)
  WrData2 <= WrData1;

/* Если команда записи, то выставляем на шину SDRAM.DQ данные WrData2 */
assign SDRAM_DQ = SDRAM_DQ_OE ? WrData2 : 16'hZZZZ;

endmodule

 

 

 

Построчно я понимаю что происходит, но связать в общую схему некоторые куски кода у меня не получается.

 

Непонятые мною моменты(сначала как я это понял, затем вопросы):

 

1) Тут мы по фронту клока, находясь в состоянии "Ожидание", заводим сигнал "Запрос на чтение" на регистр ReadSelected. Так понимаю это нужно, чтобы зафиксировать вход, устранить "дрыганье"?

 

reg ReadSelected = 0;
always @(posedge clk) 
  if (state == 2'h0)
    ReadSelected <= read_now;
wire WriteSelected = ~ReadSelected;

 

2) Если мы в состоянии "Ожидание", то берём read_now, иначе ReadSelected

Почему мы в одном случае берём вход напрямую, а в другом из регистра ReadSelected?

Почему wire называется ReadCycle, т.е. какой цикл тут имеется ввиду?

wire ReadCycle = (state == 2'h0) ? read_now : ReadSelected;

 

3) В state-machine используется и ReadSelected и RdReq одновременно и я не понимаю зачем. Т.е. для каких целей служит ReadSelected? Он сигнализирует о том, что команда чтения выполнялась перед этим?

 

4) Устанавливаем сигнал "Достоверность прочитанных данных" спустя 4 такта. Два такта на работу самой SDRAM и два на работу SDRAM контроллера. Можете объяснить как тут происходит отсчёт четырёх тактов за счёт объединения сигналов через {}?

localparam trl = 4;  // total read latency is the SDRAM CAS-latency (two) plus the SDRAM controller induced latency (two)
reg [trl-1:0] RdDataValidPipe;
always @(posedge clk) 
  RdDataValidPipe <= {RdDataValidPipe[trl-2:0], state == 2'h1 & ReadSelected};
assign RdDataValid = RdDataValidPipe[trl-1];

 

В RTL Viewer это разворачивается в post-92818-1485347587_thumb.png

и тут уже понятно, что есть задержка в 4 такта.

Share this post


Link to post
Share on other sites
Можете объяснить как тут происходит отсчёт четырёх тактов за счёт объединения сигналов через {}?

localparam trl = 4;  // total read latency is the SDRAM CAS-latency (two) plus the SDRAM controller induced latency (two)
reg [trl-1:0] RdDataValidPipe;
always @(posedge clk) 
  RdDataValidPipe <= {RdDataValidPipe[trl-2:0], state == 2'h1 & ReadSelected};
assign RdDataValid = RdDataValidPipe[trl-1];

это сдвиговый регистр, длина 4. значение (state == 2'h1 & ReadSelected) присваивается младшему разряду, потом сдвигается вправо. выход снимается со старшего разряда.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this