¿Qué tiene de malo este intento de SDR RAM en Verilog?

Tengo un Spartan-6 FPGA conectado a la interfaz de memoria AEMIF en un SoC TI DaVinci DM365 que controlo. La AEMIF está configurada en el modo Select Strobe. Estoy tratando de implementar lectura/escritura de memoria en el FPGA sobre esa interfaz, pero no funciona. El hardware funciona y esta funcionalidad ha estado funcionando antes escrita en VHDL (no escrita por mí). Soy nuevo en HDL, así que tal vez haya algo obviamente falso aquí.

Es difícil saber lo que realmente está pasando ya que el reloj es de 60 MHz y mi analizador lógico/osciloscopio lucha por ir tan rápido.

Editar: desde entonces tengo esto funcionando. Establecer drive_data de forma combinatoria significaba que, al leer, el valor de la memoria anterior se expulsaba en el bus de datos, luego, más tarde, la lógica secuencial tomaría la nueva dirección y cambiaría los datos durante el ciclo.

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
Algunos detalles más serían útiles: ¿Qué SoC? ¿Qué tipo de memoria estás tratando de emular? ¿Qué variedad de "no funciona" está haciendo? Además, elimine el código comentado, ya que es solo una distracción para aquellos de nosotros que intentamos leerlo.
@MartinThompson: eliminé el código comentado. Está en el historial de revisión si alguien quiere cavar. Para Blueshift: si desea intentar depurar la versión asíncrona, siéntase libre de editar la versión marcada y reemplazarla con la versión asíncrona.
Encontré esto nuevamente después de 10 años trabajando con FPGA. ¡Guau! :)

Respuestas (3)

Lo hice funcionar, con un cambio específico aunque, para ser honesto, algunas otras ediciones también pueden haber ayudado.

Pensé en cómo se configura drive_data usando lógica combinatoria, pero el registro de outdata está cronometrado. Confirmado con simulación de comportamiento, esto significa que los datos obsoletos se eliminan durante la primera parte de un ciclo de lectura, antes de que se bloquee la dirección.

"Arreglé" esto cambiando el bloque always que establece outdata para hacerlo en cada reloj, lo que significa que los datos correctos ingresan allí durante la fase de configuración mientras la dirección es válida en el bus, antes de que aparezca la luz estroboscópica OE.

También refactoricé el manejo de mi bus de memoria en un submódulo (invirtiendo las señales de control).

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

No estoy seguro de los tiempos exactos de SDRAM, pero solo comentando su estilo Verilog, un par de cosas podrían ayudar. Tienes que empezar a pensar en términos de hardware. Esto puede ser más fácil de entender.

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

Además, es posible que desee mover su bloque de inicio inicial fuera de este archivo, ya que normalmente es una construcción de simulación y, en este caso, no será sintetizable de todos modos. Por lo general, es mejor separar las construcciones de simulación de las construcciones sintetizables.

Si desea restablecer el contenido de la RAM, puede usar la herramienta FPGA para configurar el contenido inicial de la RAM o diseñar un pequeño bloque que restablezca la RAM al encender.

Buena suerte. Y déjame saber si te ayuda. :)

En realidad, initiallos bloques pueden sintetizarse para FPGA. Le dicen al compilador qué estado deben tener las cosas al final de la configuración, que no es lo mismo que restablecer.
¡Gracias! El "outbit" fue una pista falsa, solo una salida de depuración que dejé por error. Y tienes razón, el bloque inicial no funciona para inicializar la memoria.

Es difícil saber lo que realmente está pasando ya que el reloj es de 60 MHz y mi analizador lógico/osciloscopio lucha por ir tan rápido.

Utilice ChipScope (Xilinx) o SignalTap (Altera). Es genial para eso y puede darte mucho ancho, si no profundidad.

Veo que eso es algo de Altera y ChipScope es aparentemente el equivalente de Xilinx. Suena interesante, pero no tengo JTAG en este momento (no preguntes). Lo tendré en cuenta para el futuro, gracias.