FPGA Grafik

Demo Programm zum Zeichnen von Computergrafik mit dem TerasIC DE1 Demo-Board

SW0 auf dem Board schaltet um zwischen funktionaler und gebufferter Grafik:

Sourcecode Fred Feuerstein Auto: DE1_Default

Sourcecode Auto V.2: DE1_Default_v2

Folgende Bilder zeigen die Phasen der Framebuffer-Linien Programmierung, basierend auf der Geradengleichung.

20120511-183725.jpg

20120511-183747.jpg

20120511-183810.jpg

20120511-183838.jpg

20120511-183855.jpg

20120511-183921.jpg

20120511-183941.jpg

20120511-184005.jpg

Diodengleichung

1. Graphische Lösung mit Octave:

octave:1> x=0:10:800
octave:2> y1=(5000-x)/1000
octave:3> y2=1e-12*(exp(x/26)-1)
octave:4> plot(x,y1)
octave:5> hold on
octave:6> plot(x,y2)
octave:7> hold off
octave:8> x=650:1:770
octave:9> y1=(5000-x)/1000
octave:10> y2=1e-12*(exp(x/26)-1)
octave:11> plot(x,y1)
octave:12> hold on
octave:13> plot(x,y2)

2. Numerische Lösung in Tabellenkalkulation:

Diodengleichung_Numerisch.odf

3. Fazit

Wir operieren hier in einem exorbitant grossen Zahlenbereich, der jede 32-Bit-CPU überlaufen lässt. Ausser, wir arbeiten mit Fliesskomma-Zahlen. Diese ermöglichen, kleine Zahlen mit kleinen Zahlen und grosse Zahlen mit grossen Zahlen zu verrechnen. Dadurch wird der Fehler auch kleiner.

4. Test

Hier ein erster, noch unvollendeter Versuch, sowas in Verilog zu implementieren:

module diodengl (
clk,    // clock
ufd        // Dioden-Spannung (gesucht)
);

input                clk;
output     [31:0]    ufd;

reg        [31:0]    u1;        // Spannungsquelle      mV
reg        [31:0]    r1;        // Innenwiderstand      Ohm
reg        [31:0]    is;        // Dioden-Sperrstrom    pA
reg        [31:0]    ut;        // Temperaturspannung   mV
reg        [31:0]    smin;      // Abbruchbedingung     mV

reg        [31:0]    s;         // "Schrittweite"
reg        [31:0]    sa;        // "Schrittweite", absolutwert
reg        [31:0]    y1;        // Zwischenresultat 1
reg        [31:0]    y2;        // Zwischenresultat 2
reg        [31:0]    y2a;       // Zwischenresultat 2a (Ufd/Ut)
reg        [31:0]    y2b;       // Zwischenresultat 2b (e^y1)
reg     [31:0]    ufd;          // Resultat
reg     [31:0]    i;            // Zähler

initial begin
    u1=5000;
    r1=1000;
    is=1;
    ut=26;
    smin=1;

    s=1;
    sa=1;
    ufd=1;
end

always @ (posedge clk)
begin
        // Error (10119): Verilog HDL Loop Statement error at
        // diodengl.v(35): loop with non-constant loop condition must
        // terminate within 250 iterations    

        //    while(sa>tol) begin    

        ufd = ufd + s;

        y1=u1-ufd/r1;

        y2a=ufd/ut;
        // Error (10256): Verilog HDL error at diodengl.v(42):
        // exponentiation is not supported for specified operands,
        // exponent base must be a positive power of 2 with
        // non-constant exponent.

        //   y2b=2718**y2a;

        y2b=2048**y2a;
        y2=is*(y2b-1000);

        s=(y1-y2)/100;

        // Absolutwert von Schrittweite berechnen für
        // Abbruchbedingung
        // if (s[31]==1'b1) sa[31:0]={1'b1,s[30:0]};
        // else sa [31:0]=s [31:0];
        // end
end
endmodule

PWM

Für ein 16Bit/48kHz PWM wird ein Takt von 2^16*48kHz=3.146 GHz benötigt. Dieser kann noch halbiert werden, da bei der steigenden und fallenden Flanke der PWM-Zähler hochgezählt werden kann.

Auf unserem Board DE0-Nano stehen uns lediglich 50 MHz zur Verfügung. Diese können mit der PLL auf ca. 500MHz hochgetaktet werden. Wir müssen also bei der Bitbreite oder Abtastfrequenz des PWM sparen. z.B: 14Bit/24KHz: 2^14*24kHz/2=196.6MHz

Um die Funktionsweise des PWM zu testen haben wir uns vorerst auf ein 8Bit PWM beschränkt. In folgenden Zeitdiagrammen ist die Funktionsweise deutlich zu sehen:

Bei jedem Nulldurchgang des Zählers T wird der am Eingang X anliegende Weit ins Register X1 gespeichert und der Ausgang Y auf 1 gesetzt (ausser X=0, dann bleibt der Ausgang 0). Danach wird T hochgezählt, bis der Wert von X1 erreicht ist und Y wieder auf 0 gesetzt wird. So ist es nun möglich, ein Rechtecksignal Y zu generieren, dessen Pulsbreite über den digitalen Wert X gesteuert werden kann.

//
// 8 Bit PWM
//
// Clock > 2^8 * srate / 2
// z.B. @ 48 kHz: 6.1 MHz
//
module PWM_8
(SYS_CLK, X, Y, X1, T);
//
input             SYS_CLK;
input [7:0]        X;
//
output            Y;
output    [7:0]    X1;
output    [7:0]    T;
//
wire [7:0]        X;
reg  [7:0]        X1;
reg              Y;
reg  [7:0]        T;

initial begin
    Y <= 0;
    T <= 8'h00;
    X1 <= 8'h00;
end

// always @(SYS_CLK) does not work, why?
always @(posedge SYS_CLK)
begin
    if (T==0) begin
        X1 <= X;
        if(X1 != 0) Y  <= 1;
    end
    else if (T==X1) begin
        Y <= 0;
    end
    T  <= T + 1;
end
endmodule

Quartus Projekt: fpga_synth.zip

 

FPGA-Synth

In diesem Projekt versuchen wir, einen Musiksynthesizer auf FPGA zu realisieren.

Ein Synthesizer besteht in der Regel aus Oszilatoren, Hüllkurvengeneratoren, Filtern, Modulationsmatrix, einem Mixer und einer Steuerung bzw. einem Sequenzer. Auf mehrstimmigkeit wollen wir vorerst verzichten.

Diese Komponenten werden digital simuliert und anschliessend in ein Analogsignal konvertiert (DAC). Damit wir keine zusätzliche Hardware entwickeln müssen, wollen wir in diesem Projekt den DAC mit einem PWM realisieren.

Im folgenden ein grober Ablaufplan des Projektes:

  1. PWM
  2. Sinus Generator
  3. Hüllkurvengenerator
  4. Sequenzer / Steuerung
  5. Additive Synthese, Sägezahn mit additiver Synthese, iFFT
  6. Realisierung des Filters mit Faltung über Wellenformspektrum

Pipeline

always @(posedge or negedge)
if (posedge)

Initialisierungsdaten

pro pipeline-kanal:

reg [2:0] posedge_counter;
reg [2:0] negedge_counter;
reg program_status1;
reg program_status2;
reg daten_status1;
reg daten_status2;

1. posedge: prefetch
– Instruction -> Hilfsregister
– PC++
– posedge_counter++

1. negedge: fetch
– kopieren in INSTRUCTION-Register

VHDL Demo1

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity Demo is
	port 
	(
		Clk			: in std_logic;
		ResetN		: in std_logic;
		TasteN1		: in std_logic;
		Led			: out std_logic_vector(7 downto 0)
	);

end entity;

architecture Demo_Arch of Demo is

type states		is(Reset, LedOnLow, LedOnHigh, LedAllOn);
signal state		: states;
signal TasteN1_old	: std_logic;	-- flipflop

-- signal counter		: std_logic_vector(31 downto 0);


begin
	-- vergleich = und nicht ==	
	-- Led <= counter(31 downto 24);

	DemoP : process(ResetN, Clk) is
	begin
		if(ResetN = '0') then
			-- setze alle LED auf 1 bei reset
			Led <= (others => '0');
			state <= Reset;
			TasteN1_old <= '1';
			
			-- counter <= (others => '0');
		elsif(Clk'event and Clk = '1') then
			-- <= ist Zuweisung!
			-- 4mal weil 4 led
			-- Led(7 downto 4) <= TasteN1 & TasteN1 & TasteN1 & TasteN1;
			-- Led(3 downto 0) <= not(TasteN1 & TasteN1 & TasteN1 & TasteN1);
			-- counter <= counter + 1;
			
			TasteN1_old <= TasteN1;

			case state is
				when Reset =>
					Led <= (others => '0');
					if(TasteN1_old = '0' and TasteN1 = '1') then
						state <= LedOnLow;
					end if;
			
				when LedOnLow =>
					Led(7 downto 4) <= (others => '0');
					Led(3 downto 0) <= (others => '1');
					if(TasteN1_old = '0' and TasteN1 = '1') then
						state <= LedOnHigh;
					end if;
					
				when LedOnHigh =>
					Led(7 downto 4) <= (others => '1');
					Led(3 downto 0) <= (others => '0');
					if(TasteN1_old = '0' and TasteN1 = '1') then
						state <= LedAllOn;
					end if;
					
				when LedAllOn =>
					Led <= (others => '1');
					if(TasteN1_old = '0' and TasteN1 = '1') then
						state <= Reset;
					end if;
					
			end case;
			
		end if;
	end process DemoP;
	


end Demo_Arch;

FPGA Workshop

S-RAM based

22k Logic Cells

Lookup-Table, 16 in, FlopFlop, 1 out

Programmierbare Interconnection Points.

2 Programmiermethoden
– Struktural, auf Hardwareebene (1980)
– Verhaltensbasiert, Funktional (Verilog)

Programmiert wird:
– Lookup Table
– Startwert vom FlipFlop
– Interconnection points

Seither weiterentwicklung:
– RAM (512k) in 9k Blöcken
– PLLs, gut, wenig jitter
– Hardware Multiplier
– keine Divisionen: müssen sequentialisiert werden..

Unser Modell:
– 6 PLLs

Einsatzgebiet:
– Spezialanfertigung, wenn kein Baustein vorhanden ist, alternative zu Microcontroller, die mehr Paralellität ermöglicht
– neu auch: Bildcodierung
alternative ASIC (schneller, hohe Auflage günstiger, initialkosten 10 Mio)

VHDL vs. Verilog vs. System-Verilog

VHDL
– eher Softwareentwicklerbasiert
– Ministry of Defense

Verilog
– eher Elektrotechnikerbasiert
– Private Firma

New Project:
– Top Level Entity Name merken
– keine Files hinzufügen
– Devices herstellerübergreifend Kompatibel, unterschiede bezüglich Pinout
Arria: 15 Jahre alt
MAX: CPLD (fpga mit integriertem flash, früher auf)
FPGA: Flüchtig (braucht immer Programmierdevice)
Cyclone: Günstig, stromsparend
Stratix: HighEnd Serie, teuer, Grafikprozessorenprototypen

–> wir arbeiten mit Cyclone IV

VHDL an ADA angelehnt, extremst strongtypig (streng in der Verwendung der Datentypen)

Verilog an C angelegt

VHDL Datei Aufbau:

  • Libs
  • interface description “entity”
    Taste ->
    Led <-
  • Architecture
Flow:
  • Coding
  • Analysis & Synthesis
  • Compile
  • Pin Planer
  • TimeAnalyzer (TimeQuest Timing Analyzer, time constraints)
    • Create clock: Period 20 ns
  • Programmer
Konstrukt: Process für getaktete
Demo.qsf
set_global_assignment -name NOMINAL_CORE_SUPPLY_VOLTAGE 1.2V

ModelSim

New Project

Add File Demo_tb.vhd

entity Demo_tb is
 port
 (
 );
 end entity;

=> Verbindung (wire)

<= Zuweisung (reg)

Array: Datenstruktur

Vektor: Datantyp

Tools:
Compile
TimeQuest TimeAnalyzer
Create Timing Netlist
Constraints/Create clock
write sdc file
assignments/settings/../TimeQuest Time analyzer/sdc filename
compile

CPU mit Pipeline

Im Folgenden ein Ansatz für eine CPU mit Pipeline in Verilog. Im beispiel, dass 8 mal inkrementieren sollte, sehen wir schön, dass in einer Pipeline Operationen, deren Argumente von einer vorhergehenden Operation abhängt, nicht dicht aufeinander folgen dürfen. Denn zum Zeitpunkt des ladens der Argumente steht das Resultat nicht zur Verfügung. Daher zählt das vorliegende Beispiel nur bis 3 statt bis 8.

Quartus Projekt Download: ex3_pipeline.zip

Wie verbinde ich Ports mit verschiedenen Breiten?

Error: Width mismatch in port “wraddress[9..0]” of instance “inst1” and type ex3_lpm_ram_dp_0 — source is “”DATA_ADR_WR[15..0]” (ID EX2_CPU_PIPELINE:inst)”
Error: Width mismatch in port “rdaddress[9..0]” of instance “inst1” and type ex3_lpm_ram_dp_0 — source is “”DATA_ADR[15..0]” (ID EX2_CPU_PIPELINE:inst)”

Antwort: Mit benannten Verbindungen (—X). z.B.

DATA_ADR[15..0] |—-X DATA_ADR[15..0] DATA_ADR[9..0] X—-| rdaddress[9..0]