Teach Pelajaran dengan makmal jarak jauh

Pelajaran Teach

Logik digital pada DE1-SoC dengan VHDL (4/6): pembilang dan daftar anjakan pilihan

Bina pembilang VHDL dengan pembahagi jam, kemudian cuba sambungan BCD dan daftar anjakan jika masa mengizinkan.

  • Altera DE1-SoC
  • 40 min
  • Prasiswazah peringkat pengenalan
  • ms-MY
  • Digital systems & FPGA
Altera DE1-SoC
Altera DE1-SoC

Hasil pembelajaran

  • Menerangkan fungsi pembahagi jam dan menggunakannya untuk menjadikan tingkah laku 50 MHz kelihatan.

  • Membina pembilang 4 bit dan membaca nilainya pada LED serta paparan tujuh segmen.

  • Mengenal kerja BCD dan daftar anjakan sebagai sambungan pilihan.

  • Mengenal masalah lebar daftar, termasuk pemalar yang tidak muat dalam daftar yang diisytiharkan.

Pratonton aktiviti pelajar

Kandungan aktiviti

Pratonton sahaja. Dalam sesi kelas, pelajar boleh mengisi jawapan dan menyerahkan hasil kerja mereka kepada guru.

1

Daripada ingatan kepada gerakan: mengapa pembahagi jam diperlukan

10 min

Dalam Pelajaran 3, anda membina litar yang mengingati satu bit atau bait: flip-flop dan daftar muatan selari. Setiap satu mengemas kini nilai tersimpan pada pinggir jam. Kini anda menggunakan semula idea itu untuk membina litar yang bergerak sendiri pada setiap pinggir: pembilang yang maju 0, 1, 2, 3, .... Jika masa mengizinkan, sambungan pilihan menggunakan prinsip yang sama untuk daftar anjakan, yang menggerakkan bit ke sisi.

Jam CLOCK_50 papan ialah gelombang 50 MHz dengan 50,000,000 pinggir menaik sesaat. Jika pembilang 4 bit maju pada setiap pinggir, ia akan menghabiskan semua 16 nilai kira-kira tiga juta kali sesaat. LED hanya kelihatan kabur dan kamera jauh tidak sempat menunjukkan setiap langkah.

Oleh itu, kita menggunakan pembahagi jam. Pembahagi mengira pinggir CLOCK_50; apabila mencapai kiraan terminal DIV, ia menyongsangkan isyarat perlahan slow_clk dan mula mengira semula. Semakin besar DIV, semakin perlahan slow_clk. Litar utama dipacu oleh slow_clk, bukan terus oleh CLOCK_50, supaya gerakannya dapat dilihat.

Reka bentuk jujukan perlu mengisytiharkan jam. Templat blink.vhd tidak menyertakannya, maka setiap entiti dalam pelajaran ini menambah CLOCK_50 : in std_logic. Isyarat lain ialah SW(9 downto 0) (suis ke atas = 1), KEY(3 downto 0) (aktif-rendah: ditekan = 0), LEDR(9 downto 0) (aktif-tinggi) dan HEX0HEX5 (tujuh segmen aktif-rendah).

Pembahagi jam yang menerima CLOCK_50 pada 50 MHz, mengira hingga 33,333,333 lalu menyongsangkan slow_clk untuk menghasilkan kira-kira 0.75 Hz dan satu pinggir menaik setiap 1.33 saat.

Pembahagi mengira pinggir pantas CLOCK_50 dan hanya menyongsangkan slow_clk apabila mencapai DIV. Nilai DIV yang lebih besar menghasilkan gerakan lebih perlahan yang dapat diikuti oleh mata dan kamera.

Pembilang 4 bit yang dipacu terus oleh CLOCK_50 melangkah kira-kira tiga juta kali sesaat, sedangkan kamera hanya menangkap beberapa bingkai sesaat. Apakah yang sebenarnya kelihatan pada LED, dan mengapa?

Jika DIV digandakan daripada 33,333,333 kepada 66,666,666, bagaimanakah kadar langkah pembilang berubah secara anggaran, dan mengapa? Jawab dalam satu ayat.

2

Latihan 2-C: pembilang 4 bit dengan pembahagi jam

12 min

Pembilang ialah daftar yang nilai seterusnya bersamaan nilai semasa tambah satu. Pembilang 4 bit menyimpan 0 hingga 15; selepas 15, ia kembali ke 0 secara automatik kerana 15 + 1 = 10000 dalam perduaan dan hanya empat bit rendah (0000) disimpan.

Reka bentuk ini mempunyai dua bahagian. Pembahagi mengira pinggir CLOCK_50 hingga DIV dan menyongsangkan slow_clk. Pembilang bertambah satu pada setiap pinggir menaik slow_clk; KEY(0) menetapkannya semula kepada 0. Nilai dipaparkan dalam perduaan pada LEDR(3 downto 0) dan sebagai satu digit perenambelasan pada HEX0: 09, kemudian AF bagi 1015.

Pemasaan. Dengan DIV = 33,333,333, slow_clk disongsangkan selepas kira-kira 0.67 s. Satu langkah hanya berlaku pada pinggir menaik, dan gelombang segi empat memerlukan dua songsangan bagi satu tempoh penuh. Maka pembilang maju kira-kira setiap 1.33 s, iaitu lebih kurang 0.75 Hz.

Perangkap lebar daftar. Daftar pembahagi di sini selebar 27 bit. Daftar 26 bit hanya menyimpan hingga 2^26 − 1 = 67,108,863, jadi DIV = 100,000,000 tidak muat. Daftar 27 bit mencapai 2^27 − 1 = 134,217,727, maka 100,000,000 dan 8,333,333 kedua-duanya muat. Sentiasa pastikan pemalar muat dalam lebar yang diisytiharkan.

Catatan kejuruteraan. Memacu daftar terus daripada slow_clk terbahagi ialah jalan pintas pengajaran supaya gerakan jelas. Dalam reka bentuk pengeluaran, semua logik biasanya kekal pada CLOCK_50 dan menggunakan pemboleh jam, iaitu denyut satu kitaran yang membenarkan kemas kini. Ini memudahkan analisis pemasaan oleh alat sintesis.

-- Kod permulaan pelajar: lengkapkan baris TODO sebelum sintesis.
-- Pelajaran 4 - Latihan 2-C: pembilang 4 bit dengan pembahagi jam.
-- KEY(0)=set semula. LEDR(3 downto 0)=kiraan; HEX0=kiraan perenambelasan.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity blink is
    port (
        CLOCK_50 : in  std_logic;
        KEY      : in  std_logic_vector(3 downto 0);
        LEDR     : out std_logic_vector(9 downto 0);
        HEX0     : out std_logic_vector(6 downto 0)
    );
end entity blink;

architecture rtl of blink is
    constant DIV : unsigned(26 downto 0) := TODO_VISIBLE_DIVIDER;
    signal div_count : unsigned(26 downto 0) := (others => '0');
    signal slow_clk  : std_logic := '0';
    signal count     : unsigned(3 downto 0) := (others => '0');

    function seg(d : unsigned(3 downto 0)) return std_logic_vector is
    begin
        case d is
            when "0000" => return "1000000"; when "0001" => return "1111001";
            when "0010" => return "0100100"; when "0011" => return "0110000";
            when "0100" => return "0011001"; when "0101" => return "0010010";
            when "0110" => return "0000010"; when "0111" => return "1111000";
            when "1000" => return "0000000"; when "1001" => return "0010000";
            when "1010" => return "0001000"; when "1011" => return "0000011";
            when "1100" => return "1000110"; when "1101" => return "0100001";
            when "1110" => return "0000110"; when "1111" => return "0001110";
            when others => return "1111111";
        end case;
    end function;
begin
    process(CLOCK_50)
    begin
        if rising_edge(CLOCK_50) then
            if KEY(0) = '0' then
                div_count <= (others => '0');
                slow_clk <= '0';
            elsif div_count = TODO_DIVIDER_LIMIT then
                div_count <= (others => '0');
                slow_clk <= not slow_clk;
            else
                div_count <= div_count + 1;
            end if;
        end if;
    end process;

    process(slow_clk, KEY)
    begin
        if KEY(0) = '0' then count <= (others => '0');
        elsif rising_edge(slow_clk) then count <= TODO_NEXT_COUNT;
        end if;
    end process;

    LEDR(3 downto 0) <= TODO_LED_COUNT;
    LEDR(9 downto 4) <= (others => '0');
    HEX0 <= TODO_HEX_COUNT;
end architecture rtl;

Andaikan daftar pembahagi hanya 24 bit. Apakah nilai terbesarnya (2^24 − 1), dan adakah DIV = 33,333,333 masih muat? Terangkan dalam satu ayat.

3

Jalankan dan tetapkan semula pembilang

10 min

Bina pembilang pada papan sebenar, perhatikan ia maju, tetapkan semula, kemudian cuba dua nilai pembahagi lain.

  1. Buka makmal VHDL DE1-SoC; editor memaparkan blink.vhd.

  2. Gantikan seluruh fail dengan pembilang di atas, lengkapkan semua TODO, kekalkan entiti blink dan sahkan CLOCK_50 : in std_logic.

  3. Klik Synthesize. Tutup atau langkau lawatan jika menutup butang. Tunggu 1–3 minit, baiki ralat dan ulang sehingga binaan berjaya dengan 0 ralat. Amaran pemasaan dijangka bagi pembahagi pengajaran ini.

  4. Klik Upload to FPGA dan tunggu papan serta kamera.

  5. Perhatikan LEDR(3 downto 0) dan HEX0. Nilai baharu muncul kira-kira setiap 1.33 s. Sahkan LED perduaan sepadan dengan digit perenambelasan; contohnya C sepadan dengan 1100.

  6. Tekan dan tahan KEY(0) untuk menetapkan kiraan kepada 0, kemudian lepaskan dan lihat kiraan bermula semula.

  7. Pilihan: tukar DIV kepada 8,333,333 (to_unsigned(8_333_333, 27)), sintesis dan perhatikan kiraan lebih pantas. Kemudian cuba 100,000,000 (to_unsigned(100_000_000, 27)) dan lihat ia lebih perlahan. Sambungan ini tidak diperlukan untuk bukti wajib.

Dengan DIV = 33,333,333, rekod beberapa nilai ketika muncul. Bagi setiap nilai, catat corak LEDR(3 downto 0), aksara HEX0 dan nilai perpuluhan. Kemudian tetapkan semula dengan KEY(0) dan sahkan kiraan kembali kepada 0.

Pemerhatian # LEDR(3 downto 0) (perduaan) Aksara HEX0 Nilai perpuluhan (0–15)

Hantar bukti. Apabila HEX0 menunjukkan C (perpuluhan 12), ambil tangkap layar kamera; LEDR(3 downto 0) mesti menunjukkan 1100 pada masa yang sama.

Jika anda menjalankan sambungan dengan DIV = 8,333,333 dan DIV = 100,000,000, nilai manakah menghasilkan langkah lebih pantas dan kira-kira berapa kali lebih pantas?

Untuk menggunakan pembilang sebagai jam dengan ralat tidak melebihi satu saat dalam 24 jam, apakah yang perlu tepat selain pembilang? Namakan unsur utama dan jelaskan sebabnya.

4

Sambungan pilihan: pembilang BCD 0 hingga 9

7 min

Pembilang 4 bit biasa bergerak 015 dan menunjukkan AF bagi enam nilai terakhir. Banyak paparan hanya memerlukan satu digit perpuluhan: 0, 1, 2, …, 9, kemudian kembali ke 0. Ini ialah pembilang BCD (perpuluhan berkod perduaan).

Daripada membiarkan daftar melilit selepas 15, kesan nilai 9 dan paksa nilai berikutnya kepada 0. Gantikan proses pembilang dengan:

process(slow_clk, KEY)
begin
    if KEY(0) = '0' then count <= TODO_RESET_COUNT;
    elsif rising_edge(slow_clk) then
        if count = TODO_LAST_BCD_DIGIT then count <= TODO_WRAP_VALUE;
        else                              count <= TODO_NEXT_COUNT;
        end if;
    end if;
end process;

Kekalkan pembahagi, tugasan serentak LEDR/HEX0 dan fungsi seg. Kini HEX0 hanya berkitar 0 hingga 9.

  1. Edit entiti supaya kiraan kembali kepada 0 selepas 9.

  2. Jalankan sintesis, baiki ralat dan muat naik ke FPGA.

  3. Perhatikan HEX0 berkitar 0, 1, 2, …, 9, 0, 1, … tanpa mencapai A. Gunakan KEY(0) untuk menetapkan semula jika perlu.

(Sambungan BCD pilihan) Apakah nilai count pada pinggir selepas HEX0 menunjukkan 9, dan baris manakah yang memaksa nilai itu? Jika anda menjalankannya, tambah pemerhatian ketika HEX0 kembali kepada 0.

5

Sambungan pilihan: daftar anjakan 8 bit

9 min

Daftar anjakan ialah sederet flip-flop yang memindahkan setiap bit kepada jirannya pada setiap pinggir sementara satu bit baharu masuk di hujung. Ia penting bagi pautan bersiri seperti UART, SPI dan I2C, yang menghantar data satu bit pada satu masa.

Daftar shreg(7 downto 0) menjalankan tugasan ini pada setiap pinggir menaik slow_clk apabila anjakan didayakan:

shreg <= TODO_SHIFT_LEFT_UPDATE;  -- TODO: anjak kiri dan masukkan SW(0) ke bit 0

Sebelah kanan mengambil tujuh bit rendah shreg(6 downto 0), menggerakkannya satu kedudukan ke atas dan memasukkan SW(0) ke bit 0. Semua bit bergerak ke hujung tinggi, jadi ini ialah anjakan kiri. SW(0) ialah input bersiri, KEY(0) ialah set semula dan KEY(1) ialah pemboleh anjakan; kedua-dua KEY aktif-rendah. Tahan KEY(1) untuk menganjak pada setiap detik.

Corak jangkaan. Bermula pada 0000_0000, masukkan empat 1: 0000_0001, 0000_0011, 0000_0111, 0000_1111. Kemudian masukkan empat 0: 0001_1110, 0011_1100, 0111_1000, akhirnya 1111_0000.

Nilai bit berbanding rupa. Rekod nilai perduaan dan rupa kamera. Susunan fizikal LED mungkin kelihatan terbalik berbanding penulisan bit (shreg(7) di satu sisi dan shreg(0) di sisi lain). Itu kesan orientasi, bukan ralat logik.

-- Kod permulaan pelajar: lengkapkan baris TODO sebelum sintesis.
-- Pelajaran 4 - Latihan 2-D: daftar anjakan 8 bit yang menganjak ke kiri.
-- SW(0)=input data bersiri, KEY(0)=set semula, KEY(1)=pemboleh anjakan. KEY aktif-rendah.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity blink is
    port (
        CLOCK_50 : in  std_logic;
        KEY      : in  std_logic_vector(3 downto 0);
        SW       : in  std_logic_vector(9 downto 0);
        LEDR     : out std_logic_vector(9 downto 0)
    );
end entity blink;

architecture rtl of blink is
    constant DIV : unsigned(26 downto 0) := TODO_VISIBLE_DIVIDER;
    signal div_count : unsigned(26 downto 0) := (others => '0');
    signal slow_clk  : std_logic := '0';
    signal shreg     : std_logic_vector(7 downto 0) := (others => '0');
begin
    process(CLOCK_50)
    begin
        if rising_edge(CLOCK_50) then
            if KEY(0) = '0' then
                div_count <= (others => '0'); slow_clk <= '0';
            elsif div_count = DIV then
                div_count <= (others => '0'); slow_clk <= not slow_clk;
            else
                div_count <= div_count + 1;
            end if;
        end if;
    end process;

    process(slow_clk, KEY)
    begin
        if KEY(0) = '0' then
            shreg <= (others => '0');
        elsif rising_edge(slow_clk) then
            if KEY(1) = '0' then shreg <= TODO_SHIFT_UPDATE;
            end if;
        end if;
    end process;

    LEDR(7 downto 0) <= TODO_SHIFT_OUTPUT;
    LEDR(9 downto 8) <= (others => '0');
end architecture rtl;

Bermula daripada 0000_0000 dengan KEY(1) ditahan, masukkan SW(0) = 1, 1, 0, 1 selama empat detik, yang terdahulu dahulu. Kira setiap langkah dan tulis shreg(7 downto 0) selepas detik keempat.

6

Sambungan pilihan: gerakkan bit merentasi LED

9 min

Bina daftar anjakan, lihat bit menyala bergerak sepanjang baris, kemudian lihat sifar mengikutinya.

  1. Gantikan blink.vhd dengan daftar anjakan di atas, lengkapkan TODO, kekalkan blink dan CLOCK_50 : in std_logic.

  2. Jalankan sintesis, baiki ralat dan muat naik ke FPGA.

  3. Tekan dan lepaskan KEY(0) untuk mengosongkan daftar.

  4. Tetapkan SW(0)=1 dan tahan KEY(1). Selepas empat detik, empat LED menyala (0000_1111).

  5. Tetapkan SW(0)=0, terus tahan KEY(1) dan tunggu empat detik lagi hingga 1111_0000.

  6. Lepaskan KEY(1) untuk membekukan corak supaya mudah dibaca dan dirakam.

Tetapkan semula dan tahan KEY(1). Rekod empat detik dengan SW(0)=1, kemudian empat detik dengan SW(0)=0. Tulis shreg sebagai lapan bit dengan shreg(7) di kiri dan catat sama ada LED kelihatan terbalik pada kamera.

Detik # Input SW(0) shreg(7 downto 0) (8 bit) Kamera: LED menyala / terbalik?

Hantar bukti. Selepas empat 1 dan empat 0, lepaskan KEY(1) untuk membekukan shreg = 1111_0000, ambil tangkap layar dan catat sama ada LED kelihatan terbalik.

Bandingkan nilai yang dikira (1111_0000) dengan LED dalam tangkap layar. Jika kelihatan terbalik, jelaskan sebab itu kesan orientasi/label dan bukan pepijat logik anjakan.

7

Pilihan: anjak kanan dan pembilang gelang

6 min

Anjak ke arah lain. Untuk menggerakkan setiap bit ke hujung rendah dan memasukkan bit baharu di bahagian atas (shreg(7)), tukar satu tugasan:

shreg <= TODO_SHIFT_RIGHT_UPDATE; -- TODO: anjak kanan dan masukkan SW(0) ke bit 7

Kini SW(0) masuk ke shreg(7) dan shreg(7 downto 1) turun ke shreg(6 downto 0).

Sambungan: pembilang gelang. Pembilang gelang menyuap balik output daftar sendiri supaya corak tetap beredar. Bina gelang anjak kiri dengan shreg(7) disuap ke bit 0. Set semula perlu memuatkan "00000001", dan baris anjakan ialah shreg <= shreg(6 downto 0) & shreg(7);. Tahan KEY(1) untuk melihat satu LED beredar.

  1. Tukar satu baris anjakan kepada versi kanan, jalankan sintesis dan muat naik.

  2. Tetapkan semula, tahan KEY(1), masukkan empat 1 dan empat 0, lalu sahkan bit bergerak ke arah bertentangan.

  3. Pilihan: bina pembilang gelang dengan set semula "00000001" dan suapan balik shreg(7) ke bit 0.

Dalam versi anjak kanan shreg <= SW(0) & shreg(7 downto 1);, bermula daripada 0000_0000, masukkan SW(0)=1 selama empat detik. Hujung manakah yang terisi, dan apakah nilai akhirnya?

8

Perkara yang anda bina

4 min

Dalam tiga ayat, terangkan: (1) cara pembahagi menjadikan gerakan pembilang kelihatan, (2) satu bukti konkrit yang dirakam daripada pembilang dan HEX0, dan (3) satu perangkap yang dielakkan, seperti pemalar yang tidak muat dalam daftar 27 bit. Jika anda mencuba sambungan BCD atau daftar anjakan, tambah satu ayat tentang bukti itu.