Sie sind hier

Die Ablaufsteuerung

Fehlermeldung

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.3 Die Ablaufsteuerung und die Zeitbasis

Dieser letzte Block besteht aus drei Teilen: zunächst aus der eigentlichen Ablaufsteuerung, die alle Steuersignale erzeugt, dann aus der Zeitbasis, die ausgehend vom 5 kHz Referenztakt die Torzeiten 0,01, 0,1, 1 und 10 Sekunden erzeugt, und aus einer kleine Handshake-Einheit, die das Vorhandensein eines neuen Zählergebnisses signalisiert. Zunächst aber das Coding der Ablaufsteuerung, die als State-Machine realisiert 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 controller is
  7. Port(clk : in std_logic;
  8. reset : in std_logic;
  9. counting : in std_logic;
  10. shift_load : out std_logic;
  11. cnt_reset : out std_logic;
  12. tb_reset : out std_logic);
  13. end controller;
  14.  
  15. architecture Behavioral of controller is
  16. type t_state is (s0,s1,s2,s3,s4,s5,s6,s7);
  17. signal state, next_state: t_state;
  18. begin
  19.  
  20. -- state shift_load cnt_reset tb_reset
  21. -- s0: running 0 0 0
  22. -- s1: pause1 0 0 0
  23. -- s2: load sr 1 0 0
  24. -- s3: pause2 0 0 0
  25. -- s4: cnt_reset 0 1 0
  26. -- s5: pause3 0 0 0
  27. -- s6: tb_reset 0 0 1
  28. -- s7: wait 0 0 0
  29.  
  30. p1:process(clk, reset, next_state) begin
  31. if reset = '1' then
  32. state <= s3;
  33. elsif clk = '1' and clk'event then
  34. state <= next_state;
  35. end if;
  36. end process p1;
  37.  
  38. p2:process(state, counting) begin
  39. case state is
  40. when s0 =>
  41. if counting = '0' then
  42. next_state <= s1;
  43. else
  44. next_state <= s0;
  45. end if;
  46.  
  47. when s1 => next_state <= s2;
  48. when s2 => next_state <= s3;
  49. when s3 => next_state <= s4;
  50. when s4 => next_state <= s5;
  51. when s5 => next_state <= s6;
  52. when s6 =>
  53. if counting = '0' then
  54. next_state <= s7;
  55. else
  56. next_state <= s6;
  57. end if;
  58.  
  59. when s7 =>
  60. if counting = '1' then
  61. next_state <= s0;
  62. else
  63. next_state <= s7;
  64. end if;
  65.  
  66. end case;
  67. end process p2;
  68.  
  69. p3:process(state) begin
  70. case state is
  71. when s2 =>
  72. shift_load <= '1';
  73. cnt_reset <= '0';
  74. tb_reset <= '0';
  75. when s4 =>
  76. shift_load <= '0';
  77. cnt_reset <= '1';
  78. tb_reset <= '0';
  79. when s6 =>
  80. shift_load <= '0';
  81. cnt_reset <= '0';
  82. tb_reset <= '1';
  83. when others =>
  84. shift_load <= '0';
  85. cnt_reset <= '0';
  86. tb_reset <= '0';
  87. end case;
  88. end process p3;
  89.  
  90. end Behavioral;

Der Prozess p1 überführt den zuvor in next_state ermeittelten neuen Zustand in das signal state. Außerdem setzt er den Anfangszustand bei einem Reset. Der Prozeß p2 ist die eigentliche State-Machine. Im einfachsten Fall wird nur der Zustand um 'ein' weiter geschaltet; in anderen Fällen hängt der Fortschritt davon ab, ob etwa der Zäher noch läuft oder nicht (etwa s0 nach s1 nur dann, wenn der Zähler fertig ist; andernfalls in s0 bleiben). Der Prozeß p3 übersetzt die internen Zustände s0 bis s7 in Zustände auf den leitungen shift_load (Latch/Schieberegister mit dem aktuellen Zählerstand laden), cnt_reset (Zähler auf Null setzen) und tb_reset (Zeitbasis zurücksetzen). Um Überlappungen bei den drei Steuersignalen auszuschließen, wurden jeweils Zwischenzustände eingeführt, in denen alle drei Signale auf Null sind. Siehe auch der Kommentar vor dem Prozeß p1.

Die zeitbasis stellt die vier Torzeiten zur Verfügung, die über den Eingang sel_tb (2 bit breit) ausgewählt werden können. Der einzige vorhandene Prozeß ist p1, der eine Mischung aus State-Machine und Zähler ist. Die Zustände sind prep (prepare, vorbereiten), running (Zeitbasis läuft; das Zähltor ist 'offen') und ready (das Zähltor ist 'geschlossen' und die Zählung im Hauptzähler ist beendet).

  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 timebase is
  7. Port (sel_tb : in bit_vector(1 downto 0);
  8. clk : in std_logic;
  9. start : in std_logic;
  10. counting : out std_logic);
  11. end timebase;
  12.  
  13. architecture Simple of timebase is
  14. type t_state is (prep,running,ready);
  15. signal x : std_logic_vector(23 downto 0);
  16. signal state : t_state;
  17. begin
  18. p1: process(clk,x,sel_tb,state,start) begin
  19. if start = '1' then
  20. state <= prep;
  21. elsif clk = '1' and clk'event then
  22. case state is
  23. when prep =>
  24. case sel_tb is
  25. when "00" => x <= conv_std_logic_vector(100,24); -- 10 ms
  26. when "01" => x <= conv_std_logic_vector(1000,24); -- 100 ms
  27. when "10" => x <= conv_std_logic_vector(10000,24); -- 1 s
  28. when "11" => x <= conv_std_logic_vector(100000,24); -- 10 s
  29. end case;
  30. if start = '1' then
  31. state <= prep;
  32. else
  33. state <= running;
  34. end if;
  35. when running =>
  36. x <= x-1;
  37. if x = x"000001" then
  38. state <= ready;
  39. else
  40. state <= running;
  41. end if;
  42. when ready =>
  43. state <= ready;
  44. end case;
  45. end if;
  46. end process p1;
  47.  
  48. counting <= '1' when state = running else '0';
  49.  
  50. end Simple;

In der prep-Phase wird der Anfangswert des Zählers auf denjenigen Wert gesetzt der der ausgewählten Torzeit entspricht. Dieser Wert wird in der running-Phases mit jedem clk-Impuls um eins heruntergezählt. Bei erreichen von 0 wechselt der Zustand auf ready. Der nebenläufige Prozeß am Ende signalisiert den Zustand running als den Wert 1 am Ausgang counting.

Das letzte Stück Coding dient dazu, dem angeschlossenen Microcontroller jeweils die Bereitstellung neuer Daten zu signalisieren. Es ist eine Art Handshake: Sind neue Daten verfügbar, was über data_ready_i angezeigt wird, dann geht data_ready_o auf 0. Der angeschlossene Controller setzt, nachdem er die Daten ausgelesen hat, ack auf 0, was dazu führt, daß data_ready_o wieder auf 1 geht. Liegt erneut ein Zählergebnis vor, dann geht data_ready_o erneut auf 0 und das Spiel wiederholt sich. Letzten Endes ein wechselseitig geschaltetes Flip-Flop.

  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 handshake is
  7. Port (ack : in std_logic;
  8. data_ready_i : in std_logic;
  9. data_ready_o : out std_logic);
  10. end handshake;
  11.  
  12. architecture Behavioral of handshake is
  13. begin
  14.  
  15. p1:process(ack, data_ready_i) begin
  16. if ack = '0' then
  17. data_ready_o <= '1';
  18. elsif data_ready_i'event and data_ready_i = '0' then
  19. data_ready_o <= '0';
  20. end if;
  21. end process p1;
  22.  
  23. end Behavioral;

Die drei entities werden über das letzte Stück Coding, das eine Struktur definiert, zusammengeführt. Jeder der drei entities kommt genau einmal vor und sie werden intern über Signale zusammengeschaltet bzw. direkt mit den Ein- und Ausgängen verbunden. Einige interne Signale werden zusätzlich noch über einen nebenläufigen Prozeß an entsprechende Ausgänge übergeben.

  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 timebase_and_controller is
  7. Port (reset : in std_logic;
  8. sel_tb : in bit_vector(1 downto 0);
  9. clk_ref : in std_logic;
  10. data_ack : in std_logic;
  11. shift_load : out std_logic;
  12. cnt_reset : out std_logic;
  13. counting : out std_logic;
  14. data_ready : out std_logic);
  15. end timebase_and_controller;
  16.  
  17. architecture Structure of timebase_and_controller is
  18. signal tb_start : std_logic;
  19. signal counting_int : std_logic;
  20. signal cnt_reset_int : std_logic;
  21.  
  22. component timebase
  23. Port (sel_tb : in bit_vector(1 downto 0);
  24. clk : in std_logic;
  25. start : in std_logic;
  26. counting : out std_logic);
  27. end component;
  28.  
  29. component controller
  30. Port (clk : in std_logic;
  31. reset : in std_logic;
  32. counting : in std_logic;
  33. shift_load : out std_logic;
  34. cnt_reset : out std_logic;
  35. tb_reset : out std_logic);
  36. end component;
  37.  
  38. component handshake
  39. Port (ack : in std_logic;
  40. data_ready_i : in std_logic;
  41. data_ready_o : out std_logic);
  42. end component;
  43.  
  44. for TB: timebase use entity work.timebase(simple);--Behavioral);
  45. for CT: controller use entity work.controller(Behavioral);
  46. for HS: handshake use entity work.handshake(Behavioral);
  47.  
  48. begin
  49.  
  50. TB: timebase port map(
  51. sel_tb => sel_tb,
  52. clk => clk_ref,
  53. start => tb_start,
  54. counting => counting_int);
  55.  
  56. CT:controller Port map(
  57. clk => clk_ref,
  58. reset => reset,
  59. counting => counting_int,
  60. shift_load => shift_load,
  61. cnt_reset => cnt_reset_int,
  62. tb_reset => tb_start);
  63.  
  64. HS:handshake Port map(
  65. ack => data_ack,
  66. data_ready_i => cnt_reset_int,
  67. data_ready_o => data_ready);
  68.  
  69. counting <= counting_int;
  70. cnt_reset <= cnt_reset_int;
  71. end Structure;

Zum Abschluß auch hier die Constraints, die die Ein- und Ausgänge mit den Pins des XR9572XL verbindet.

  1. NET "clk_ref" LOC = "P43" | BUFG = CLK ;
  2. NET "cnt_reset" LOC = "P32" ;
  3. NET "counting" LOC = "P5" ;
  4. NET "data_ack" LOC = "P2" ;
  5. NET "data_ready" LOC = "P3" ;
  6. NET "reset" LOC = "P33" | BUFG = SR ;
  7. NET "sel_tb<0>" LOC = "P6" ;
  8. NET "sel_tb<1>" LOC = "P7" ;
  9. NET "shift_load" LOC = "P28" ;

Natürlich ist es sinnvoll, gerade die Ablaufsteuerung zusammen mit der Zeitbasis und dem Handshake-Flipflop zu simulieren. Dazu wurde zunächst das folgende VHDL-Testfile erzeugt:

  1. LIBRARY ieee;
  2. USE ieee.std_logic_1164.ALL;
  3. USE ieee.std_logic_unsigned.all;
  4. USE ieee.numeric_std.ALL;
  5.  
  6. ENTITY timebase_and_controller_test IS
  7. END timebase_and_controller_test;
  8.  
  9. ARCHITECTURE behavior OF timebase_and_controller_test IS
  10. COMPONENT timebase_and_controller
  11. PORT(reset : IN std_logic;
  12. sel_tb : IN bit_vector(1 downto 0);
  13. clk_ref : IN std_logic;
  14. data_ack : IN std_logic;
  15. shift_load : OUT std_logic;
  16. cnt_reset : OUT std_logic;
  17. counting : OUT std_logic;
  18. data_ready : OUT std_logic);
  19. END COMPONENT;
  20.  
  21. signal reset : std_logic := '0';
  22. signal sel_tb : bit_vector(1 downto 0) := (others => '0');
  23. signal clk_ref : std_logic := '0';
  24. signal data_ack : std_logic := '1';
  25.  
  26. signal shift_load : std_logic;
  27. signal cnt_reset : std_logic;
  28. signal counting : std_logic;
  29. signal data_ready : std_logic;
  30.  
  31. -- Clock period definitions
  32. constant clk_ref_period : time := 100 us;
  33.  
  34. BEGIN
  35. -- Instantiate the Unit Under Test (UUT)
  36. uut: timebase_and_controller PORT MAP (
  37. reset => reset,
  38. sel_tb => sel_tb,
  39. clk_ref => clk_ref,
  40. data_ack => data_ack,
  41. shift_load => shift_load,
  42. cnt_reset => cnt_reset,
  43. counting => counting,
  44. data_ready => data_ready);
  45.  
  46. -- Test Workbench: Clock process definition
  47. clk_ref_process :process begin
  48. wait for 1500 us; -- just wait for some time
  49. while true loop -- forever ...
  50. clk_ref <= '0'; -- low
  51. wait for clk_ref_period/2; -- for half a clock cycle and ...
  52. clk_ref <= '1'; -- high
  53. wait for clk_ref_period/2; -- for the other half of the clock cycle
  54. end loop;
  55. end process;
  56.  
  57. -- Test Workbench: Reset process definitions
  58. reset_process :process begin
  59. reset <= '0'; -- no reset
  60. wait for 1000 us; -- 1 ms later
  61. reset <= '1'; -- no we have a reset for
  62. wait for 1000 us; -- ... another ms
  63. reset <= '0'; -- no reset or running mode
  64. wait; -- wait forever
  65. end process;
  66.  
  67. -- Test Workbench: Simulates the microcontroller reading data
  68. handshake_process: process begin
  69. wait for 5 ms; -- wait for 5 ms. Maybe setting up the LC-display etc.
  70. while true loop -- forever ...
  71. if data_ready = '0' then -- if (new) data are available ...
  72. wait for 1 ms; -- again ... wait a little bit and pretend reading data
  73. data_ack <= '0'; -- now confirm reception of data and set data_ack to 0
  74. wait for 100 us; -- make the ack-pulse 100 us long (active low)
  75. data_ack <= '1'; -- go back to passive state
  76. else wait for 1 ms; -- no new data available: pretend doing something else
  77. end if;
  78. end loop;
  79. end process;
  80.  
  81. END;

Das Testfile simuliert den Takt mit genau 10 kHz, das Reset-Signal und den Handshakeprozeß im Rahmen der Bereitstellung der Daten zum auslesen durch die CPU. Das Ergebnis der Simulation zeigt das folgende Bild:

Simulationsergebnis der entity timebase_and_controller

Die Simulation zeigt verschiedene Dinge. Die oberste Spur stellt das Signal zur Auswahl der Zeitbasis dar. Der Wert ist '00' und entspricht daher einer Torzeit von 10 ms. Des weiteren sind zwei Cursor eingezeichnet, die sich auf die vierte Spur, counting, beziehen und die genau 10.000.000.000 ps Abstand haben, was nichts anderes ist als die eingestellte Torzeit von 10 ms. Diesen Teil macht die Ablaufsteuerung schon mal ganz gut. Die zweite Spur zeigt wenig erkennbares, weil sie für den Reset steht, der hier im Bild jetzt nicht dargestellt ist. Die dritte Spur ist der 10 kHz Referenztakt. Zwischen den beiden Cursorn müßten also genau 100 Takte liegen. Betrachten wir jetzt die Region vor dem ersten Cursor. Mit der fallenden Flanke des Signals counting wurde die vorhergehende Messung beendet. Der Zähler ist verriegelt und zählt nicht mehr. Kurz danach wird, wie in der fünften Spur gezeigt, der shift_load-Impuls erzeugt, der den Zählerstand in das Latch/Schieberegister übernimmt. Nach einer klerinen Pause wird, wie die sechste Spur anzeigt, das Reset-Signal für den Zähler erzeugt, der damit auf Null gesetzt wird. Außerdem wird das data_ready-Signal (passive Logik!) auf Null gezogen um dem angeschlossenen Microcontroller zu signalisieren, daß eine neue Messung vorliegt. Der Controller liest nun die Daten aus dem Schieberegister aus (hier nicht simuliert) und signalisiert das Ende der Datenübernahme damit, daß er das data_ack-Signal (ebenfalls passive Logik) kurz auf Null zieht. Das setzt wiederum data_ready in den passiven Zustand, was einfach bedeutet: keine neuen Daten vorhanden.