Implementierung der HBF-Kaskade I
Wir beginnen mit einem leeren Modul nur mit der Schnittstelle:
--! Cascade of half band filters with decimation
entity HbfCascade is
port (
Clk : in std_logic; --! System clock (assumed 100 MHz)
Init : in std_logic; --! Synchronous reset
AdcIn : in std_logic_vector(15 downto 0); --! ADC Signal from periphery
AdcValid : in std_logic; --! New Sample of ADC is available
FilterOut : out std_logic_vector(21 downto 0); --! Filtered output
FilterValid : out std_logic --! New Sample of Filter Data is available
);
end HbfCascade;
Wir haben also unseren Verarbeitungstakt Clk und bekommen ein AdcValid immer wenn ein neuer Wert vom ADC ankommt. Analog dazu wird jedes mal, wenn die Kaskade einen Wert ausspuckt, der zugehörige FilterValid generiert.
Bereich | Startadresse | Endadresse |
---|---|---|
Param 1 | 0x01F8 | 0x01FF |
Param 2 | 0x01F0 | 0x01F7 |
Param 3 | 0x01E8 | 0x01EF |
Param 4 | 0x01E0 | 0x01E7 |
Param 5 | 0x01D8 | 0x01DF |
Param 6 | 0x01D0 | 0x01D7 |
Param 7 | 0x01C8 | 0x01CF |
Param 8 | 0x01C0 | 0x01C7 |
Coeff 1 | 0x01BC | 0x01BF |
Coeff 2 | 0x01B8 | 0x01BB |
Coeff 3 | 0x01B4 | 0x01B7 |
Coeff 4 | 0x01B0 | 0x01B3 |
Coeff 5 | 0x01AC | 0x01AF |
Coeff 6 | 0x01A8 | 0x01AB |
Coeff 7 | 0x01A0 | 0x01A7 |
Coeff 8 | 0x0180 | 0x019F |
Stufe 1 | 0x0160 | 0x017F |
Stufe 2 | 0x0140 | 0x015F |
Stufe 3 | 0x0120 | 0x013F |
Stufe 4 | 0x0100 | 0x011F |
Stufe 5 | 0x00E0 | 0x00FF |
Stufe 6 | 0x00C0 | 0x00DF |
Stufe 7 | 0x0080 | 0x00BF |
Stufe 8 | 0x0000 | 0x007F |
Ich habe hier schon einmal die Memory-Map für den verwendeten Speicher entworfen und werde etwas dazu erklären.
Die Eingangsspeicher für die einzelnen Stufen benötigen eine zirkuläre Addressierung, damit man einfach in den Speicher hineinschreiben kann und wenn man am Ende angekommen ist gehts wieder von vorne los.
Das erreicht man, wenn die untersten Bit fix sind und der Adresszeiger dann modulo agiert. Damit die unteren Bits aber fix sind, muss der Adressbereich des zirkulären Speichers auf so einer Modulo-Adresse beginnen. Beispiel Stufe 3, die bei 0x120 beginnt. Der zirkuläre Speicher hat die länge von 0x20. Mit einem 5 Bit Zähler kann ich also innerhalb dieses Bereichs von 0 bis 31 zählen und fange dann wieder von Vorne an, OHNE die unteren Bits zu beeinflussen. Wäre die Basisadresse 0x110 würde das nicht funktionieren.
Die Eingangsdaten für die erste Stufe werden also ab 0x160 eingespeichert. Für die Verwaltung dieser ersten Stufe benötigt man drei 5 Bit Register: Schreibzeiger, Lesezeiger und Füllstand. Über den Füllstand kann man dann den Trigger für die Ablaufsteuerung geben um diese Stufe zu bearbeiten.
Da die Einspeicherung der Daten über das "linke" Port des Blockrams erfolgt, braucht man für das Beschreiben nur dieses Basisadresse und den 5 Bit Schreibzeiger. Der Rest geschieht auf der "rechten" Seite wo verschiedene Prozesse ablaufen und dementsprechend dem RAM immer die richtigen Adressen gereicht werden müssen.
Der Folgende Code dient also zum Einspeichern der Daten und der Verwaltung dieses Speicherbereichs.
-- interface from controller to signal stage process completed
signal StageR : integer range 0 to 7; -- Stufenauswahl
signal StageReadR : std_logic; -- Leseerlaubnis
-- Zeiger und Füllstand
signal Stage1WrPtr : unsigned(4 downto 0); -- 5-Bit Schreibzeiger
signal ResStage1WrC : std_logic; -- Reset $Stage1WrPtr
signal IncStage1WrC : std_logic; -- Increment $Stage1WrPtr
signal Stage1RdPtr : unsigned(4 downto 0); -- 5-Bit Lesezeiger
signal ResStage1RdC : std_logic; -- Reset $Stage1RdPtr
signal IncStage1RdC : std_logic; -- Increment $Stage1RdPtr
signal Stage1WordsR : unsigned(4 downto 0); -- 5-Bit Füllstandszähler
signal ResSt1WrdC : std_logic; -- Reset $Stage1WordsR
signal IncSt1WrdC : std_logic; -- Increment $Stage1WordsR
signal DecSt1WrdC : std_logic; -- Decrement $Stage1WordsR
-- RAM-Adresse
signal RamAAddrR : std_logic_vector(8 downto 0); -- 16-Bit RAM-Adresse (anpassbar)