Перейти к содержанию
    

Вопросы по 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 такта.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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) присваивается младшему разряду, потом сдвигается вправо. выход снимается со старшего разряда.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...