Что не так с этой попыткой SDR RAM в Verilog?

У меня есть ПЛИС Spartan-6, подключенная к интерфейсу памяти AEMIF на SoC TI DaVinci DM365, которым я управляю. AEMIF настроен в режиме Select Strobe. Я пытаюсь реализовать чтение/запись памяти на FPGA через этот интерфейс, но это не работает. Аппаратное обеспечение работает, и эта функциональность работала до того, как была написана на VHDL (не мной). Я новичок в HDL, поэтому, возможно, здесь есть что-то явно поддельное.

Трудно сказать, что на самом деле происходит, поскольку тактовая частота составляет 60 МГц, а мой осциллограф/логический анализатор изо всех сил пытается работать так быстро.

Изменить: с тех пор у меня это работает. Установка drive_data комбинаторным способом означала, что при чтении старое значение памяти выбрасывалось на шину данных, а затем последовательная логика подбирала новый адрес и изменяла данные в течение цикла.

module main(
    input EM_A_3,
    input EM_A_7,
    input EM_CLK,
    inout [15:0] EM_D,
    input EM_nCE1,
    input EM_nOE,
    input EM_nWE
    );

wire [1:0] em_addr;
/* temporary storage for emif "registers" */
reg [15:0] mem [0:3];
reg [15:0] em_outdata;

supply0 rst;        // reset always 0 for now
wire drive_data;

initial
begin: FOO
    integer i;
    for (i = 0; i < 4; i = i + 1) begin
        mem[i] = 8'b0;
    end
    em_outdata = 8'b1;
end

// drive EM_D when CE1, OE are low, and WE is high
assign drive_data = !EM_nCE1 && !EM_nOE && EM_nWE;
assign EM_D = drive_data ? em_outdata : 8'bz;
assign em_addr = {EM_A_7, EM_A_3};

// clocked version (not working yet)
always @ (posedge EM_CLK)
begin
    if (!EM_nCE1 && !EM_nWE) begin
        mem[em_addr] <= EM_D;
    end
    if (!EM_nCE1 && !EM_nOE && EM_nWE) begin
        em_outdata <= mem[em_addr];
    end
end

endmodule
Некоторые дополнительные подробности были бы полезны: Какой SoC? Какой тип памяти вы пытаетесь эмулировать? Какое разнообразие "не работает" он делает? Кроме того, удалите закомментированный код, так как он просто отвлекает тех из нас, кто пытается его прочитать.
@MartinThompson - я удалил закомментированный код. Это в истории изменений, если кто-то хочет копать. Для Blueshift: если вы хотите попытаться отладить асинхронную версию, не стесняйтесь отредактировать синхронизированную версию и заменить ее асинхронной версией.
Обнаружил это снова после 10 лет работы с ПЛИС. Ух ты! :)

Ответы (3)

У меня это работает, с одним конкретным изменением, хотя, честно говоря, некоторые другие правки тоже могли помочь.

Я думал о том, как настраивается drive_data с помощью комбинаторной логики, но регистр outdata тактируется. Подтверждено поведенческим моделированием, это означает, что устаревшие данные удаляются в течение первой части цикла чтения, прежде чем адрес будет зафиксирован.

Я «исправил» это, изменив блок always, который устанавливает outdata, чтобы он делал это каждый такт, что означает, что правильные данные поступают туда во время фазы установки, пока адрес действителен на шине, до того, как появится строб OE.

Я также преобразовал обработку шины памяти в подмодуль (инвертируя управляющие сигналы).

module emif(
    input clk,
    input [1:0] addr,
    inout [15:0] data,
    input ce,           // note these signals are active high
    input we,           // (opposite to the PCB signals)
    input oe
    );

wire drive_data;
reg [15:0] mem [0:3];
reg [15:0] em_outdata;

assign drive_data = ce && oe && !we;
assign data = drive_data ? em_outdata : 16'bz;

// writes data to small mem
always @ (posedge clk)
begin
    if (ce && we) begin
        mem[addr] <= data;
    end
end

// reads data from small mem
always @ (posedge clk)
begin
    em_outdata <= mem[addr];
end

endmodule

Я не уверен в точных таймингах SDRAM, но просто комментирую ваш стиль Verilog, может помочь несколько вещей. Вам нужно начать думать с точки зрения аппаратного обеспечения. Это может быть легче понять.

assign EM_D = drive_data ? em_outdata : 16'bz;
assign drive_data = !EM_nCE1 & !EM_nOE & EM_nWE;

// writes data to small mem
always @ (posedge EM_CLK)
begin
    if (!EM_nCE1 && !EM_nWE) begin
        mem[em_addr] <= EM_D;
    end
end

// reads data from small mem
always @ (posedge EM_CLK)
begin
    if (!EM_nCE1 && !EM_nOE && EM_nWE) begin
        em_outdata <= mem[em_addr];
    end
end

// see any problem? please change this logic. it doesn't seem to go anywhere.
always @ (posedge EM_CLK)
begin
    if (!EM_nCE1 && !EM_nWE) begin
        outbit <= 1;
    end else if (!EM_nCE1 && !EM_nOE && EM_nWE) begin
        outbit <= 0;
    end else
        outbit <= X; // *** Not sure what's the default/reset cond is.
end

Кроме того, вы можете захотеть переместить свой блок initial-begin из этого файла, так как он обычно является конструкцией моделирования, и в этом случае он в любом случае не будет синтезируемым. Обычно лучше отделить моделируемые конструкции от синтезируемых конструкций.

Если вы хотите сбросить содержимое ОЗУ, вы можете либо использовать инструмент FPGA для установки начального содержимого ОЗУ, либо разработать небольшой блок, который сбрасывает ОЗУ при включении питания.

Удачи. И дайте мне знать, если это поможет. :)

На самом деле initialблоки могут быть синтезированы для ПЛИС. Они сообщают компилятору, какое состояние должно быть в конце настройки, а это не то же самое, что сброс.
Спасибо! «Outbit» был отвлекающим маневром, просто результатом отладки, который я оставил по ошибке. И вы правы, начальный блок не работает для инициализации памяти.

Трудно сказать, что на самом деле происходит, поскольку тактовая частота составляет 60 МГц, а мой осциллограф/логический анализатор изо всех сил пытается работать так быстро.

Используйте ChipScope (Xilinx) или SignalTap (Altera). Это отлично подходит для этого и может дать вам много ширины, если не глубины.

Я вижу, что это вещь Altera, а ChipScope, по-видимому, является эквивалентом Xilinx. Звучит интересно, но у меня сейчас нет JTAG (не спрашивайте). Буду иметь в виду на будущее, спасибо.