Teach Pelajaran dengan makmal jarak jauh

Pelajaran Teach

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

Laksanakan pembilang dan daftar anjakan dalam Verilog, perhatikan corak LED berjujukan dan terangkan kemas kini berjam pada perkakasan.

  • Altera DE1-SoC
  • 65 min
  • Prasiswazah, 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 kepada mata dan kamera.

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

  • Membina daftar anjakan dan menaakul tertib bit berbanding susunan fizikal LED pada kamera.

  • Mengenali masalah lebar daftar, seperti 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 storan kepada gerakan: mengapa kita memerlukan pembahagi jam

10 min

Dalam Pelajaran 3, anda membina litar yang mengingati satu bit atau satu bait: flip-flop dan daftar muatan selari. Setiap satunya mengemas kini nilai tersimpan pada pinggir jam. Dalam pelajaran ini, anda menggunakan semula idea itu untuk membina litar yang bergerak sendiri pada setiap pinggir: pembilang yang meningkat melalui 0, 1, 2, 3, … dan daftar anjakan yang menggerakkan bitnya ke sisi.

Jam papan ialah CLOCK_50, gelombang segi empat 50 MHz dengan 50,000,000 pinggir menaik setiap saat. Jika pembilang bergerak sekali pada setiap pinggir CLOCK_50, ia akan melalui kesemua enam belas nilai pembilang 4 bit kira-kira tiga juta kali sesaat. Anda hanya akan melihat kabur LED yang kelihatan tetap, dan kamera jauh yang menangkap beberapa bingkai sesaat langsung tidak dapat mengikutinya.

Oleh itu, kita memperlahankan litar menggunakan pembahagi jam. Pembahagi jam itu sendiri hanyalah sebuah pembilang: ia mengira pinggir CLOCK_50 dan, apabila mencapai kiraan terminal pilihan DIV, ia menyongsangkan isyarat lebih perlahan bernama slow_clk lalu mula mengira semula. Semakin besar DIV, semakin perlahan slow_clk. Kemudian litar *sebenar* — pembilang 4 bit atau daftar anjakan — dipacu oleh slow_clk, bukan CLOCK_50.

Seperti dalam setiap pelajaran, reka bentuk jujukan mesti mengisytiharkan jam sendiri. Templat lalai leds_mirror.v hanya menyenaraikan suis, butang dan LED serta tidak mempunyai jam, jadi setiap modul dalam pelajaran ini menambah input CLOCK_50; pada senarai port. Isyarat papan kekal sama: SW[9:0], dengan suis dinaikkan mewakili 1; KEY[3:0], aktif-rendah, jadi butang ditekan dibaca sebagai 0; LEDR[9:0], aktif-tinggi, menyala apabila dipacu 1; dan HEX0HEX5, paparan tujuh segmen aktif-rendah yang segmennya menyala apabila dipacu 0.

Pembahagi jam menerima CLOCK_50 pantas 50 MHz di sebelah kiri dan menghantarnya kepada pembilang yang mengira hingga nilai terminal DIV; setiap kali pembilang mencapai DIV, ia menyongsangkan output slow_clk yang jauh lebih perlahan di sebelah kanan, menukarkan berjuta-juta pinggir pantas sesaat kepada kira-kira satu pinggir perlahan sesaat yang dapat diikuti oleh mata dan kamera.

Pembahagi jam mengira pinggir pantas CLOCK_50 dan hanya menyongsangkan slow_clk yang jauh lebih perlahan apabila mencapai kiraan terminal DIV. Meningkatkan DIV memperlahankan slow_clk. Litar sebenar dipacu oleh slow_clk, maka gerakannya dapat dilihat.

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

Jika anda menggandakan kiraan terminal DIV, misalnya daripada 33,333,333 kepada 66,666,666, kira-kira bagaimanakah *kadar* langkah pembilang berubah, dan mengapa? Jawab dalam satu ayat.

2

Latihan 2-C: pembilang 4 bit dengan pembahagi jam

12 min

Pembilang ialah daftar yang nilai berikutnya hanyalah nilai semasa ditambah satu. Pembilang 4 bit menyimpan nilai 0 hingga 15; selepas 15, ia melilit semula kepada 0 secara automatik kerana 15 + 1 = 16 = 10000 dalam perduaan dan hanya empat bit rendah disimpan, iaitu 0000. Anda tidak memerlukan kod khas untuk pelilitan ini; lebar daftar melakukannya untuk anda.

Reka bentuk di bawah mempunyai dua bahagian. Pembahagi mengira pinggir CLOCK_50 hingga DIV dan menyongsangkan slow_clk. Pembilang meningkat satu pada setiap pinggir menaik slow_clk, manakala KEY[0] menetapkannya semula kepada 0; kerana butang ini aktif-rendah, menahannya mengaktifkan set semula. Kiraan memacu LEDR[3:0] dalam perduaan dan turut memacu HEX0 sebagai satu digit perenambelasan: 09, kemudian AF bagi 1015.

Pemasaan dengan tepat. Dengan DIV = 33,333,333, pembahagi menyongsangkan slow_clk selepas kira-kira 0.67 saat, kerana 33,333,333 pinggir daripada 50,000,000 sesaat bersamaan lebih kurang dua pertiga saat. Langkah pembilang hanya berlaku pada pinggir menaik slow_clk, dan gelombang segi empat mempunyai satu pinggir menaik bagi setiap *tempoh penuh*: satu perubahan rendah ke tinggi dan satu perubahan tinggi ke rendah. Maka pembilang meningkat kira-kira sekali setiap 1.33 saat, bersamaan kira-kira 0.75 Hz. Jangkakan nilai baharu sedikit kurang daripada sekali sesaat.

Masalah lebar daftar. Daftar pembahagi di sini sengaja selebar 27 bit. Daftar 26 bit hanya boleh menyimpan nilai hingga 2^26 - 1 = 67,108,863. Nilai seperti DIV = 100,000,000 langsung tidak muat dalam 26 bit, jadi reka bentuk dengan lebar itu sudah rosak sebelum dijalankan. Dengan 27 bit, daftar boleh menyimpan hingga 2^27 - 1 = 134,217,727; oleh itu DIV = 100,000,000, yang lebih perlahan, dan DIV = 8,333,333, yang lebih pantas, kedua-duanya muat dan berfungsi. Sentiasa pastikan pemalar muat dalam daftar yang diisytiharkan.

Satu peringatan penting. Memacu daftar terus daripada slow_clk terbahagi seperti yang dilakukan di sini ialah jalan pintas pengajaran yang lazim kerana ia menjadikan idea mudah dilihat. Dalam reka bentuk pengeluaran, semua logik biasanya dikekalkan pada satu jam pantas CLOCK_50 dan menggunakan isyarat pemboleh jam (*clock-enable*): denyut satu kitaran yang memberitahu daftar “kemas kini pada pinggir ini”. Dengan itu, seluruh reka bentuk kekal pada satu jam sebenar yang dapat dianalisis jauh lebih baik oleh alat sintesis. Kita menggunakan jam terbahagi di sini hanya kerana ia cara paling jelas untuk *melihat* pembilang bergerak.

// Pelajaran 4 - Latihan 2-C: templat awal pembilang 4 bit dengan pembahagi jam
// KEY[0]=set semula. LEDR[3:0]=kiraan, HEX0=kiraan sebagai digit perenambelasan.
module leds_mirror(CLOCK_50, KEY, LEDR, HEX0);
    input        CLOCK_50;
    input  [3:0] KEY;
    output [9:0] LEDR;
    output [6:0] HEX0;

    localparam [26:0] DIV = TODO_DIVIDER_LIMIT;
    reg [26:0] div;
    reg        slow_clk;

    always @(posedge CLOCK_50) begin
        if (!KEY[0]) begin div <= 0; slow_clk <= 0; end
        else if (div == DIV) begin div <= 0; slow_clk <= ~slow_clk; end
        else div <= div + 1'b1;
    end

    reg [3:0] count;
    always @(posedge slow_clk or negedge KEY[0]) begin
        if (!KEY[0]) count <= TODO_RESET_COUNT;
        else         count <= TODO_NEXT_COUNT;
    end

    assign LEDR[3:0] = TODO_LED_COUNT;
    assign LEDR[9:4] = 6'b0;
    assign HEX0 = TODO_HEX_PATTERN_FOR_COUNT;
endmodule

Katakan anda menggunakan daftar pembahagi 24 bit. Apakah nilai terbesar yang dapat disimpannya? Tunjukkan 2^24 − 1. Adakah DIV = 33,333,333 masih muat? Terangkan dalam satu ayat.

3

Jalankan pembilang, tetapkannya semula dan ubah kelajuannya

12 min

Bina pembilang pada papan sebenar, perhatikan nilainya meningkat, tetapkan semula dan kemudian cuba dua nilai pembahagi lain.

  1. Buka makmal Verilog DE1-SoC. Editor memaparkan fail leds_mirror.v.

  2. Pilih seluruh kandungan leds_mirror.v dan gantikannya dengan modul pembilang 4 bit di atas. Kekalkan nama leds_mirror. Perhatikan bahawa ia mengisytiharkan input CLOCK_50;: templat tidak menambah jam untuk anda.

  3. Pilih Jalankan sintesis (Synthesize). Jika lawatan berpandu menutupi butang, tutup atau langkau lawatan itu dahulu. Tunggu kira-kira 1 hingga 3 minit. Betulkan sebarang kesilapan ejaan dan jalankan sintesis semula. Quartus mungkin melaporkan banyak amaran pemasaan kerana pembahagi pengajaran ini; teruskan hanya apabila status akhir menyatakan binaan berjaya dengan 0 ralat.

  4. Pilih Muat naik ke FPGA (Upload to FPGA) dan tunggu sesi papan serta kamera.

  5. Perhatikan LEDR[3:0] dan HEX0. Nilai baharu sepatutnya muncul sedikit kurang daripada sekali sesaat, kira-kira setiap 1.33 s. Sahkan LED perduaan sepadan dengan digit perenambelasan pada HEX0; contohnya, apabila HEX0 menunjukkan C, LEDR[3:0] sepatutnya menunjukkan 1100.

  6. Tekan dan tahan KEY[0] untuk menetapkan kiraan semula kepada 0; lepaskannya dan perhatikan kiraan meningkat semula daripada 0.

  7. Tukar DIV kepada 8,333,333 dengan menulis 27'd8_333_333, jalankan sintesis, muat naik dan perhatikan pembilang menjadi nyata lebih pantas. Kemudian tukar DIV kepada 100,000,000 dengan menulis 27'd100_000_000, jalankan sintesis, muat naik dan perhatikan ia menjadi lebih perlahan. Kedua-duanya muat kerana daftar selebar 27 bit.

Dengan DIV lalai 33,333,333, perhatikan pembilang dan catat beberapa nilai apabila ia muncul. Bagi setiap baris, tulis corak perduaan LEDR[3:0] dan aksara pada HEX0 (0–9 kemudian A–F). Selepas itu, tetapkan semula dengan KEY[0] dan sahkan kiraan kembali kepada 0.

Pemerhatian # LEDR[3:0] (perduaan) Aksara HEX0 Nilai perpuluhan (0–15)

Hantar bukti anda. Tunggu sehingga HEX0 menunjukkan digit C, iaitu perpuluhan 12, ambil tangkapan kamera — LEDR[3:0] sepatutnya menunjukkan 1100 pada masa yang sama — dan lampirkannya di bawah.

Anda menjalankan makmal dengan DIV = 8,333,333 dan DIV = 100,000,000. Nilai manakah yang menyebabkan pembilang bergerak lebih pantas dan, dengan menggunakan kedua-dua nombor, kira-kira berapa kali lebih pantas berbanding nilai yang satu lagi?

Katakan anda mahu menggunakan pembilang seperti ini sebagai jam yang kekal tepat dalam lingkungan tambah atau tolak satu saat sepanjang 24 jam. Selain pembilang itu sendiri, apakah yang perlu betul? Namakan komponen utama dan jelaskan sebab ia penting.

4

Ubah suai: pembilang BCD 0–9

7 min

Pembilang 4 bit biasa melalui 015 dan menunjukkan AF bagi enam nilai tertinggi. Banyak paparan sebenar memerlukan satu digit perpuluhan sahaja: kira 0, 1, 2, …, 9, kemudian kembali terus kepada 0. Pembilang yang hanya mengira 09 dipanggil pembilang BCD, iaitu perpuluhan berkod perduaan.

Perubahannya kecil: daripada membiarkan daftar 4 bit melilit sendiri selepas 15, anda mengesan apabila kiraan mencapai 9 dan memaksa nilai berikutnya menjadi 0. Gantikan blok always pembilang dengan logik berbentuk berikut:

reg [3:0] count;
always @(posedge slow_clk or negedge KEY[0]) begin
    if (!KEY[0])           count <= TODO_RESET_COUNT;
    else if (count == TODO_LAST_BCD_DIGIT) count <= TODO_WRAP_VALUE;
    else                   count <= TODO_NEXT_COUNT;
end

Kekalkan semua bahagian lain — pembahagi, penetapan LEDR/HEX0 dan fungsi seg — tanpa perubahan. Kini HEX0 sepatutnya melalui 0 hingga 9 dan tidak pernah menunjukkan AF.

  1. Edit modul pembilang supaya blok always kembali kepada 0 selepas 9, seperti yang ditunjukkan di atas.

  2. Jalankan sintesis, betulkan sebarang kesilapan dan pilih Muat naik ke FPGA (Upload to FPGA).

  3. Perhatikan HEX0 melalui 0, 1, 2, … 9, 0, 1, …. Sahkan ia tidak pernah mencapai A. Tekan KEY[0] untuk menetapkan semula kepada 0 jika anda mahu menyemak semula dari awal.

Dalam pembilang BCD, apakah nilai count pada pinggir jam sejurus selepas HEX0 menunjukkan 9, dan baris kod manakah yang memaksa nilai itu?

5

Latihan 2-D: daftar anjakan 8 bit

9 min

Daftar anjakan ialah sebaris flip-flop yang, pada setiap pinggir jam, memindahkan setiap bit kepada jirannya dan menerima satu bit baharu pada salah satu hujung. Daftar ini penting dalam pautan bersiri: protokol seperti UART, SPI dan I2C menghantar data satu bit pada satu masa, dan daftar anjakan mengumpulkan bit itu menjadi satu bait atau memisahkan satu bait bit demi bit.

Reka bentuk di bawah menggunakan daftar 8 bit shreg[7:0]. Pada setiap pinggir menaik slow_clk, selagi anjakan didayakan, ia melaksanakan:

shreg <= TODO_SHIFT_LEFT_UPDATE;   // anjak ke kiri dan masukkan SW[0] ke bit yang betul

Baca sebelah kanan dengan teliti: ia mengambil tujuh bit rendah shreg[6:0], menggerakkannya satu kedudukan ke atas dan memasukkan SW[0] ke bit 0. Setiap bit bergerak ke arah hujung tinggi, maka ini ialah anjakan kiri. Kawalannya ialah SW[0] = bit bersiri masuk, KEY[0] = set semula aktif-rendah dan KEY[1] = pemboleh anjakan aktif-rendah: tahan KEY[1] untuk menganjak pada setiap detik.

Corak yang dijangka. Gunakan penaakulan dan jangan bergantung pada orientasi kamera. Mulakan dengan 0000_0000. Tetapkan SW[0] = 1 dan biarkan empat detik berlalu: daftar diisi daripada bit 0 ke atas, menghasilkan 0000_0001, kemudian 0000_0011, 0000_0111, 0000_1111. Sekarang tetapkan SW[0] = 0 dan biarkan empat detik lagi: sifar masuk pada bit 0 dan menolak satu ke atas lalu keluar, menghasilkan 0001_1110, 0011_1100, 0111_1000 dan akhirnya 1111_0000. Jadi selepas empat 1 diikuti empat 0, shreg[7:0] ialah 1111_0000.

Nilai bit berbanding perkara yang anda lihat. Catat kedua-dua nilai perduaan daftar dan perkara yang ditunjukkan kamera. Susunan fizikal LED pada papan mungkin kelihatan tercermin berbanding cara bit ditulis, dengan shreg[7] di satu hujung dan shreg[0] di hujung lain. Oleh itu, LED menyala boleh kelihatan terbalik kiri ke kanan berbanding nombor 1111_0000. Itu kesan pelabelan dan orientasi, bukan ralat logik; sebab itulah anda perlu mencatat nilai yang dikira bersama-sama foto.

// Pelajaran 4 - Latihan 2-D: templat awal daftar anjakan 8 bit
// SW[0]=data bersiri masuk, KEY[0]=set semula, KEY[1]=pemboleh anjakan.
module leds_mirror(CLOCK_50, SW, KEY, LEDR);
    input        CLOCK_50;
    input  [9:0] SW;
    input  [3:0] KEY;
    output [9:0] LEDR;

    localparam [24:0] DIV = TODO_DIVIDER_LIMIT;
    reg [24:0] div;
    reg        slow_clk;
    always @(posedge CLOCK_50) begin
        if (!KEY[0]) begin div <= 0; slow_clk <= 0; end
        else if (div == DIV) begin div <= 0; slow_clk <= ~slow_clk; end
        else div <= div + 1'b1;
    end

    reg [7:0] shreg;
    always @(posedge slow_clk or negedge KEY[0]) begin
        if (!KEY[0])      shreg <= TODO_RESET_VECTOR;
        else if (!KEY[1]) shreg <= TODO_SHIFT_LEFT_UPDATE;
    end

    assign LEDR[7:0] = TODO_SHIFT_REGISTER_OUTPUT;
    assign LEDR[9:8] = 2'b00;
endmodule

Bermula daripada 0000_0000, dengan KEY[1] ditahan, gunakan urutan input SW[0] = 1, 1, 0, 1 sepanjang empat detik, yang paling lama dahulu. Kira setiap detik dan tulis shreg[7:0] selepas empat detik.

6

Gerakkan bit merentasi LED

9 min

Bina daftar anjakan, perhatikan satu LED menyala bergerak sepanjang baris, kemudian lihat sifar mengikutinya.

  1. Gantikan kandungan leds_mirror.v dengan modul daftar anjakan 8 bit di atas. Kekalkan nama leds_mirror. Ia mengisytiharkan input CLOCK_50;.

  2. Jalankan sintesis, betulkan sebarang kesilapan dan pilih Muat naik ke FPGA (Upload to FPGA).

  3. Tekan dan lepaskan KEY[0] sekali untuk menetapkan daftar semula kepada semua sifar; tiada LED LEDR[7:0] menyala.

  4. Naikkan SW[0], data bersiri masuk = 1, dan tahan KEY[1]. Pada setiap detik, kira-kira setiap 1.33 s, LED menyala baharu masuk pada satu hujung dan LED sedia ada bergerak. Biarkan empat detik berlalu: empat LED sepatutnya menyala, 0000_1111.

  5. Sekarang turunkan SW[0], data bersiri masuk = 0, terus tahan KEY[1] dan biarkan empat detik lagi. Sifar mengikut satu sehingga daftar menunjukkan 1111_0000.

  6. Lepaskan KEY[1] pada bila-bila masa untuk membekukan corak semasa supaya mudah dibaca dan difoto.

Tetapkan semula, kemudian tahan KEY[1]. Catat nilai daftar detik demi detik: empat detik dengan SW0=1, kemudian empat detik dengan SW0=0. Tulis shreg sebagai lapan bit (shreg[7] di kiri, shreg[0] di kanan). Dalam lajur terakhir, nyatakan sama ada LED menyala pada kamera kelihatan tercermin berbanding bit yang anda tulis.

Detik # Input SW[0] shreg[7:0] (8 bit) Kamera: LED yang menyala / tercermin?

Hantar bukti anda. Selepas empat detik SW0=1 diikuti empat detik SW0=0, lepaskan KEY[1] untuk membekukan corak, shreg = 1111_0000, ambil tangkapan kamera, catat sama ada LED menyala kelihatan tercermin dan lampirkannya di bawah.

Bandingkan nilai perduaan yang anda kira, 1111_0000, dengan LED menyala dalam tangkapan anda. Jika ia kelihatan tercermin, terangkan mengapa hal itu merupakan kesan pelabelan dan orientasi, bukannya pepijat dalam logik anjakan.

7

Ubah suai: anjak kanan dan pembilang gelang

6 min

Anjak ke arah yang satu lagi. Untuk membuat daftar beranjak ke kanan dan bukan ke kiri — setiap bit bergerak ke arah hujung rendah, dengan bit baharu masuk pada hujung tinggi, shreg[7] — anda hanya perlu mengubah satu baris. Gantikan ruang letak anjakan kiri dengan ungkapan anjakan kanan anda sendiri.

else if (!KEY[1]) shreg <= TODO_SHIFT_RIGHT_UPDATE;   // anjak kanan dan masukkan SW[0]

Hanya baris itu berubah. Kini SW[0] masuk ke shreg[7], manakala tujuh bit tinggi shreg[7:1] bergerak turun ke shreg[6:0].

Lanjutan — pembilang gelang. Pembilang gelang menyuap balik output daftar sendiri ke inputnya dan bukan menerima bit luaran baharu, jadi satu corak tetap beredar selama-lamanya. Bina gelang beranjak kiri yang menyuap balik bit teratas ke bit terendah. Mulakan daftar pada 0000_0001 supaya tepat satu LED menyala mengejar sepanjang baris. Gunakan set semula yang memuat 8'h01, bukan 8'h00. Tahan KEY[1] dan perhatikan satu LED beredar tanpa henti.

  1. Tukar satu-satunya baris anjakan dalam modul kepada versi anjakan kanan di atas. Jalankan sintesis dan muat naik.

  2. Tetapkan semula, tahan KEY[1], gunakan SW[0]=1 selama empat detik kemudian SW[0]=0 selama empat detik, dan sahkan bit kini bergerak ke arah bertentangan merentasi LED.

  3. Lanjutan pilihan: bina pembilang gelang. Set semula memuat 8'h01, dan baris anjakan menyuap balik shreg[7] ke bit 0. Tahan KEY[1] dan perhatikan satu LED menyala beredar.

Dalam versi anjakan kanan, mulakan daripada 0000_0000 dan masukkan SW[0] = 1 selama empat detik. Hujung daftar yang manakah akan diisi, dan apakah shreg[7:0] selepas empat detik?

Dalam tiga ayat, terangkan: (1) perkara yang dilakukan pembahagi jam supaya anda dapat melihat pembilang dan daftar anjakan bergerak; (2) satu bukti khusus yang anda tangkap, sama ada nilai pembilang pada HEX0 atau corak 1111_0000; dan (3) satu masalah yang anda elakkan, sama ada pemalar yang tidak muat dalam daftar 26 bit atau nilai bit berbanding susunan LED yang tercermin.