Verwendung von BRAM als Puffer

Ich versuche, einen Puffer für eine Bildverarbeitungspipeline zu implementieren und muss Daten in BRAM laden.

Ich habe ein Online-Tutorial ( https://www.youtube.com/watch?v=n35zS__YEFQ ) zum Implementieren verschiedener Teile des Systems verfolgt, aber nach der Synthese und Implementierung kann ich sehen, dass mein Code LUTRAM anstelle von verwendet BRAM. Ich habe versucht, in die von vivado bereitgestellten Sprachvorlagen zu schauen, um Single-Port-BRAM abzuleiten, und für mich sieht der Code sehr ähnlich aus, aber ich bin neu bei FPGAs und weiß nicht viel, daher bin ich mir nicht ganz sicher, wie ich das erzwingen kann BRAM statt LUTRAM zu folgern.

Hier ist mein Verilog-Code:

module buffer #(parameter NUM_ELEMENTS=10, ADDRESS_BITS=4, WIDTH=8) (
    input clk,
    input rst,
    input [WIDTH-1:0] dataIn,
    input validInput,
    input readReady,
    output wire [WIDTH-1:0] dataOut
    );
    
    reg [WIDTH-1:0] mem [NUM_ELEMENTS-1:0];
    reg [ADDRESS_BITS-1:0] wrPtr;
    reg [ADDRESS_BITS-1:0] rdPtr;
    
    always @ (posedge clk) begin
        if (rst) begin
            wrPtr <= 0;
        end else if (validInput) begin
            mem[wrPtr] <= dataIn;
            wrPtr <= wrPtr + 1;
        end
    end
    
    assign dataOut = mem[rdPtr];
    
    always @ (negedge clk) begin
        if (rst) begin
            rdPtr <= 0;
        end else if (readReady) begin   
            rdPtr <= rdPtr + 1;
        end
    end
    
endmodule

Die Simulation des Moduls zeigt, dass es wie erwartet korrekt funktioniert (ich muss mich um einige Grenzfälle kümmern, in denen der Lesezeiger über das Ende des Puffers hinausgeht, dazu später mehr).

mein Prüfstandscode lautet:

`timescale 1ns / 1ps

module buffer_tb();
    reg clk;
    reg rst;
    reg [buffer.WIDTH-1:0] dataIn;
    reg validInput;
    reg readReady;
    wire [buffer.WIDTH-1:0] dataOut;
    
    integer i;
    
    buffer dut (clk,rst,dataIn,validInput,readReady,dataOut);
    
    initial clk = 1;
    always #5 clk = ~clk;
    
    initial begin
        rst = 1;
        dataIn = 0;
        validInput = 0;
        readReady = 0;
        #10;
        rst = 0;
        validInput = 1;
        for (i=0; i<10; i = i+1) begin
            dataIn = 2*i;
            #10;
        end
        validInput = 0;
        #40;
        readReady = 1;
        #100;
        readReady = 0;
        $stop;
    end
    
endmodule

und die Ausgabe der Simulation ist:Geben Sie hier die Bildbeschreibung ein

aber das Öffnen der Projektzusammenfassung oder des ausgearbeiteten Designs zeigt die falsche Verwendung von LUTRAM:

Geben Sie hier die Bildbeschreibung ein

Ich betone noch einmal, dass ich ein absoluter Anfänger bin und gerade erst angefangen habe, FPGAs zu verwenden; Nachdem ich ein wenig in den Handbüchern von xilinx gesucht habe, kann ich immer noch keine Möglichkeit finden, den RAM-Typ anzugeben, nur dass er bei Verwendung einer sehr spezifischen Syntax abgeleitet werden kann, was seltsam erscheint und ich es nicht geschafft habe, es zum Laufen zu bringen.

Meine Hauptfragen sind:

  1. Wie können Sie auf BRAM anstelle von LUT schließen? muss ich den vorgeschlagenen Beispielcode Zeile für Zeile kopieren oder sind Änderungen erlaubt?
  2. Wenn ich die Vorlagencodes direkt kopieren muss, wäre es besser, den IP-Core-Generator zu verwenden, um BRAM im Blockdesign zu instanziieren und einen Controller dafür zu schreiben (das Puffermodul fungiert als Wrapper), oder ob ich die eigentliche Codevorlage verwenden soll ? Ehrlich gesagt kenne ich die Unterschiede gar nicht. So wie ich es verstehe, wird das Blockdesign sowieso in Verilog konvertiert.
  3. Ich muss nur in Puffer schreiben, bis sie voll sind, und dann von ihnen lesen, bis sie leer sind. Sollte ich einen FIFO verwenden, anstatt zu versuchen, meinen eigenen Puffercode zu implementieren? So wie ich das Online-Tutorial zur Bildverarbeitung verstanden habe, kann ich mehr Puffer als erforderlich erstellen und sie füllen, während andere Teile der Daten verarbeitet werden, und dann die Puffer multiplexen, aber ich bin mir nicht sicher, welche Vor- und Nachteile ein FIFO-Block hat , verglichen mit einem handgeschriebenen Puffer.
Vorher hast du berechnet, wie viel Bram für einen Framebuffer benötigt wird und wie viel verfügbar ist. Ein typisches Low-End-Xilinx-FPGA der 7er-Serie hat nicht genug Bram, um beispielsweise einen 640x480-VGA-Frame aufzunehmen.
Ja, ich werde den Puffer regelmäßig mit Daten von QSPI-Modulen füllen, die mir genügend Kapazität für alle Dateneingaben bieten. Das eigentliche Bild wird über DMA von einem ZYNQ-Prozessor geladen, aber das ist Zukunft. Ich kann nicht einmal den Puffer selbst dazu bringen, so zu arbeiten, wie ich es will :(
DMA+DDR+Prozessor ist die Art und Weise, wie normalerweise Video-Engines auf einem FPGA implementiert werden. BRAMs können verwendet werden, um Zeilenpuffer oder FIFOs in einer Video-/Bildverarbeitungspipeline zu implementieren. Aber sie sind in den meisten FPGAs nicht groß genug für Frame-Puffer, insbesondere wenn mindestens doppelte Pufferung benötigt wird. Und ja, Sie müssen Codierungsrichtlinien und -attribute befolgen, um den BRAM mithilfe von RTL richtig abzuleiten.

Antworten (1)

Wenn Sie nur einen kleinen Puffer (<16 Dateneinträge) benötigen, gibt es absolut keinen Grund, BRAM gegenüber LUTRAM zu bevorzugen.

BRAMs befinden sich in bestimmten Bereichen des Chips, was bedeuten kann, dass relativ lange Pfade erforderlich sind, um von Ihrer anderen Logik zu ihnen zu gelangen. LUTRAM kann sich immer in der Nähe der Logik befinden, die es verwendet.

Andererseits werden größere Puffer (> 64 Dateneinträge) fast immer auf BRAM statt auf LUTRAM schließen. Es gibt auch Synthesedirektiven, die die Wahl auf die eine oder andere Weise erzwingen können. Lesen Sie die Dokumentation für die Tools, die Sie verwenden.

Und ja, FIFOs werden viel häufiger in der Videoverarbeitung verwendet. Ich habe generische Module sowohl für Single-Clock- (synchrone) als auch Dual-Clock- (asynchrone) FIFOs geschrieben, die ich ständig in meinen Video-Pipeline-Designs verwende. Ich erlaube den Synthesewerkzeugen, den geeigneten RAM-Typ für die angegebene FIFO-Größe abzuleiten. Wenn Sie die absolute Höchstleistung benötigen, verwenden Sie den IP-Generator des Anbieters, der jede spezialisierte Support-Logik auf dem Chip voll ausnutzt.

(* ram_style = "block" *) ist die Direktive in Verilog.
Leider führt selbst die Verwendung der Direktive und die Erhöhung der Anzahl der Elemente auf 1024 immer noch nicht zur Verwendung von BRAMs :( Ich werde zurückgehen und die bereitgestellten Beispiele Zeile für Zeile ändern, um zu sehen, wo mein Fehler liegt, aber jedes Mal eine Synthese ausführen muss zu überprüfen ist ein bisschen zeitaufwändig.