You are here

Der Hauptzähler

Error message

Deprecated function: Array and string offset access syntax with curly braces is deprecated in include_once() (line 20 of /mnt/web216/e3/48/5667948/htdocs/includes/file.phar.inc).

4.2 Der Hauptzähler

Der Hauptzähler (main_counter) ist ein 32 biz Binärzähler, der seinerseits aus 4 Zählern zu je 8 bit (counter8) aufgebaut ist. Außerdem besitzt der Zähler noch ein 32 bit breites Latch (osr = output shift register), das parallel vom Zähler befüllt wird und dann seriell ausgelesen wird. Zunächst der Code eines 8 bit Zählers:

  1. library IEEE;
  2. use IEEE.STD_LOGIC_1164.ALL;
  3. use IEEE.STD_LOGIC_ARITH.ALL;
  4. use IEEE.STD_LOGIC_UNSIGNED.ALL;
  5.  
  6. entity counter8 is
  7. Port (cin : in std_logic;
  8. cout : out std_logic;
  9. reset : in std_logic;
  10. clk : in std_logic;
  11. en : in std_logic;
  12. q : out std_logic_vector(7 downto 0));
  13. end counter8;
  14.  
  15. architecture Behavioral of counter8 is
  16. signal q_int:std_logic_vector(7 downto 0);
  17. begin
  18.  
  19. p1:process(clk,reset,cin) begin
  20. if reset = '1' then
  21. q_int <= "00000000";
  22. elsif clk='1' and clk'event then
  23.  
  24. if cin = '1' and en = '1' then
  25. q_int(0) <= not q_int(0);
  26. end if;
  27.  
  28. if cin = '1' and en = '1' and q_int(0) = '1' then
  29. q_int(1) <= not q_int(1);
  30. end if;
  31.  
  32. if cin = '1' and en = '1' and q_int(1 downto 0) = "11" then
  33. q_int(2) <= not q_int(2);
  34. end if;
  35.  
  36. if cin = '1' and en = '1' and q_int(2 downto 0) = "111" then
  37. q_int(3) <= not q_int(3);
  38. end if;
  39.  
  40. if cin = '1' and en = '1' and q_int(3 downto 0) = "1111" then
  41. q_int(4) <= not q_int(4);
  42. end if;
  43.  
  44. if cin = '1' and en = '1' and q_int(4 downto 0) = "11111" then
  45. q_int(5) <= not q_int(5);
  46. end if;
  47.  
  48. if cin = '1' and en = '1' and q_int(5 downto 0) = "111111" then
  49. q_int(6) <= not q_int(6);
  50. end if;
  51.  
  52. if cin = '1' and en = '1' and q_int(6 downto 0) = "1111111" then
  53. q_int(7) <= not q_int(7);
  54. end if;
  55.  
  56. end if;
  57. end process p1;
  58.  
  59. cout <= '1' when (q_int = "11111111" and cin = '1') else '0';
  60. q <= q_int;
  61.  
  62. end Behavioral;

Wie man sieht, ist die Logik für einen einfachen Zähler ein wenig ungewöhnlich. Aber zunächst die einfachen Dinge. Der Zähler besitzt natürlich einen clk-Eingang zum zählen der Ereignisse. Daneben einen reset-Eingang (asynchron) und einen en (enable)-Eingang. Der Überlauf des Zählers wird an cout (carry-out) ausgegeben und and cin (carry-in) des nächsten Zählers weitergegeben. Der Zählerstand steht am 8 bit breiten Bus q zur Verfügung.

Der aktuelle Zählerstand wird intern durch signal q_int repräsentiert. Er wird in einem nebenläufigen Prozeß nach q übertragen. Beim Zähler mit den niederwertigsten 8 bit ist der cin-Eingang immer auf 1. Steht der Zähler nun nach einem Reset auf 00000000 und der clk-Eingang wechselt auf 1, dann wird einfach das bit 0 'getoggelt' - also auf 1 gesetzt. Da wir in einer synchronen Umgebung sind, macht sich die Änderung von q_int(0) auf der rechten Seite aller weiteren if-Bedingungen noch nicht bemerkbar. Das heißt alle anderen bits bleiben unverändert. Der Zählerstand ist 00000001. Erscheint der nächste Taktimpuls, dann wird bit 0 erneut getoggelt und nimmt den Wert 0 an. Auf den rechten Seiten der Zuweisungen bzw. in den Bedingungen ist das Bit aber immer noch auf 1. Daher spricht die zweite if-Bedingung an, die nun das bit 1 toggelt und damit auf 1 setzt. Alle weiteren if-Bedingungen sind nicht erfüllt. Der Zählerstand ist jetzt 00000010, also 2. Das Spiel geht immer so weiter. Nehmen wir an, der Zäherstand wäre momentan 11111111 und es tritt erneut ein Taktimpuls auf. Bit 0 wird getoggelt. Die Bedingung zum toggeln von bit 1 ist ebenfalls erfüllt. Alle Bedingungen zum toggeln jedes bits des Zählers sind erfüllt. Der Zählerstand wechselt also auf 00000000. Außerdem wird für genau diese Konstellation auch noch der Ausgang cout auf 1 gesetzt, weil ein Übertrag aufgetreten ist. Sinn und Zweck dieser etwas exotischen Aktion war es, zu vermeiden, daß intern eine Kaskade von 8 hintereinandergeschalteten Gattern entsteht. Bei 32 bit hieße das eine Kaskade von 32 hintereinander geschalteter Gatter mit entsprechend langen Laufzeiten. Leider weiß ich nicht mehr, wo ich auf diesen Lösungsvorschlag gestoßen bin. Ich habe mir auch noch nicht die Mühe gemacht, zu überprüfen, ob's denn überhaupt geholfen hat.

Das nächste Stück Code stellt das Latch bzw. das output shift register dar und ist frei von Besonderheiten. Es wird parallel über den 32 bit breiten EIngang x geladen, wenn das load-Signal auf 1 wechselt. sck, miso und mosi sind die Signale, wie sie bei SPI-Schnittstellen üblich sind. sck ist der Taktimpuls zum seriellen auslesen des Latches; der Bitstrom erscheint dann an miso während man über mosi einen Bitstrom einspeisen kann, etwa um mehrere per SPI ansprechbare Geräte zu kaskadieren. sel entscheidet vor allem darüber, ob der miso-Ausgang aktiv ist, oder ob er hochohmig ist.

  1. library IEEE;
  2. use IEEE.STD_LOGIC_1164.ALL;
  3. use IEEE.STD_LOGIC_ARITH.ALL;
  4. use IEEE.STD_LOGIC_UNSIGNED.ALL;
  5.  
  6. entity osr is
  7. Port (x : in std_logic_vector(31 downto 0);
  8. load : in std_logic;
  9. sck : in std_logic;
  10. miso : out std_logic;
  11. mosi : in std_logic;
  12. sel : in std_logic);
  13. end osr;
  14.  
  15. architecture Behavioral of osr is
  16. signal q_int:std_logic_vector(32 downto 0);
  17. begin
  18.  
  19. p1:process(sck,q_int,mosi,load,x,sel) begin
  20. if load = '1' then
  21. q_int(31 downto 0) <= x;
  22. q_int(32) <= '0';
  23. elsif sck='1' and sck'event then
  24. if sel = '1' then
  25. q_int(32 downto 1) <= q_int(31 downto 0);
  26. q_int(0) <= mosi;
  27. end if;
  28. end if;
  29. end process p1;
  30.  
  31. miso <= q_int(32) when sel = '1' else 'Z';
  32.  
  33. end Behavioral;

Intern wird mit dem signal q_int gearbeitet, das sogar 33 bit breit ist und das Latch bzw. das Schieberegister darstellt. Ein bit davon dient als Puffer für den miso-Ausgang. Das laden des Schieberegisters geschieht parallel wenn load auf 1 geht. Ist load auf 0, dann kann das Schieberegister bitweise ausgelesen werden. Mit jedem Taktimpus (und sel = 1) werden die 'unteren' 32 bit um eine Stelle nach 'oben' verschoben. Das bit 0 nimmt den Wert von mosi, dem seriellen Eingang an. Das bit 33 wird dann nebenläufig an den miso-Ausgang übergeben, falls sel auf 1 steht. Andernfalls ist miso, da ein tristate-Ausgang, hochohmig.

Das letzte Stück Coding fügt die entity osr und vier Exemplare der entity counter8 zum 32 bit Zähler mit kombiniertem Latch und Schieberegister zusammen.

  1. library IEEE;
  2. use IEEE.STD_LOGIC_1164.ALL;
  3. use IEEE.STD_LOGIC_ARITH.ALL;
  4. use IEEE.STD_LOGIC_UNSIGNED.ALL;
  5.  
  6. entity main_counter is
  7. Port (clk : in std_logic;
  8. en : in std_logic;
  9. sck : in std_logic;
  10. mosi : in std_logic;
  11. miso : out std_logic;
  12. load : in std_logic;
  13. reset : in std_logic;
  14. sel : in std_logic);
  15. end main_counter;
  16.  
  17. architecture Behavioral of main_counter is
  18. signal lv_c12: std_logic;
  19. signal lv_c23: std_logic;
  20. signal lv_c34: std_logic;
  21. signal q : std_logic_vector(31 downto 0);
  22. signal high : std_logic;
  23.  
  24. COMPONENT counter8
  25. PORT(cin : IN std_logic;
  26. reset : IN std_logic;
  27. clk : IN std_logic;
  28. cout : OUT std_logic;
  29. en : in std_logic;
  30. q : OUT std_logic_vector(7 downto 0));
  31. END COMPONENT;
  32.  
  33. component osr
  34. Port(x : in std_logic_vector(31 downto 0);
  35. load : in std_logic;
  36. sck : in std_logic;
  37. miso : out std_logic;
  38. mosi : in std_logic;
  39. sel : in std_logic);
  40. end component;
  41.  
  42. for C1: counter8 use entity work.counter8(Behavioral);
  43. for C2: counter8 use entity work.counter8(Behavioral);
  44. for C3: counter8 use entity work.counter8(Behavioral);
  45. for C4: counter8 use entity work.counter8(Behavioral);
  46.  
  47. for osr1: osr use entity work.osr(Behavioral);
  48.  
  49. begin
  50. high <= '1';
  51.  
  52. C1: counter8 PORT MAP(
  53. cin => high,
  54. cout => lv_c12,
  55. reset => reset,
  56. clk => clk,
  57. en => en,
  58. q => q(7 downto 0));
  59.  
  60. C2: counter8 PORT MAP(
  61. cin => lv_c12,
  62. cout => lv_c23,
  63. reset => reset,
  64. clk => clk,
  65. en => en,
  66. q => q(15 downto 8));
  67.  
  68. C3: counter8 PORT MAP(
  69. cin => lv_c23,
  70. cout => lv_c34,
  71. reset => reset,
  72. clk => clk,
  73. en => en,
  74. q => q(23 downto 16));
  75.  
  76. C4: counter8 PORT MAP(
  77. cin => lv_c34,
  78. cout => open,
  79. reset => reset,
  80. clk => clk,
  81. en => en,
  82. q => q(31 downto 24));
  83.  
  84. osr1: osr port map(
  85. x => q,
  86. load => load,
  87. sck => sck,
  88. miso => miso,
  89. mosi => mosi,
  90. sel => sel);
  91.  
  92. end Behavioral;

Die vier 8-bit-Zähler werden durch die vier Instanzen C1-C4 der Komponente/entity counter8 dargestellt und das Latch durch die Instanz osr1 der Komponente osr. Die interne 'Verkabelung' wird durch die Signale lv_c12, lv_c23, lv_c34, q und high realisiert. Die ersten drei Signale verbinden den cout-Ausgang des niederen Zählers it dem cin-Eingang des nächsten Zähler. q ist der 32 bit breite Bus zwischen dem Zähler und dem Schieberegsiter und high ist so etwas wie eine Konstante, die auf den cin-Eingang des niedersten Zählers gelegt wird. Die restlichen Signale führen direkt zur Außewelt an entsprechende Pins des Bausteins XC9572XL. Die Pinbelegung ist im folgenden Coding dargelegt und repräsentiert die Constraints für dieses Projekt.

  1. NET "clk" LOC = "P43" | BUFG = CLK ;
  2. NET "en" LOC = "P7" ;
  3. NET "load" LOC = "P28" ;
  4. NET "miso" LOC = "P31" ;
  5. NET "mosi" LOC = "P32" ;
  6. NET "reset" LOC = "P33" | BUFG = SR ;
  7. NET "sck" LOC = "P1" | BUFG = CLK ;
  8. NET "sel" LOC = "P30" ;