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 10x01F80x01FF
Param 20x01F00x01F7
Param 30x01E80x01EF
Param 40x01E00x01E7
Param 50x01D80x01DF
Param 60x01D00x01D7
Param 70x01C80x01CF
Param 80x01C00x01C7
Coeff 10x01BC0x01BF
Coeff 20x01B80x01BB
Coeff 30x01B40x01B7
Coeff 40x01B00x01B3
Coeff 50x01AC0x01AF
Coeff 60x01A80x01AB
Coeff 70x01A00x01A7
Coeff 80x01800x019F
Stufe 10x01600x017F
Stufe 20x01400x015F
Stufe 30x01200x013F
Stufe 40x01000x011F
Stufe 50x00E00x00FF
Stufe 60x00C00x00DF
Stufe 70x00800x00BF
Stufe 80x00000x007F

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)