fookat 1 25 января, 2017 Опубликовано 25 января, 2017 · Жалоба Здравствуйте. Разбирался с 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 это разворачивается в и тут уже понятно, что есть задержка в 4 такта. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vadimuzzz 0 26 января, 2017 Опубликовано 26 января, 2017 · Жалоба Можете объяснить как тут происходит отсчёт четырёх тактов за счёт объединения сигналов через {}? 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) присваивается младшему разряду, потом сдвигается вправо. выход снимается со старшего разряда. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться