Menjadikan kepala penganalisa bus USB berdasarkan pada kompleks Redd

Dalam beberapa artikel terakhir, kami melihat contoh "firmware" untuk kompleks Redd, menjadikan bagian FPGA-nya sebagai penganalisa logika untuk penggunaan umum. Kemudian saya memiliki keinginan untuk mengambil langkah berikutnya dan mengubahnya menjadi penganalisa bus USB. Faktanya adalah alat analisis bermerek jenis ini sangat mahal, dan saya perlu memeriksa mengapa USB yang sama berfungsi, jika terhubung ke mesin, berfungsi, dan jika Anda menghidupkan mesin ketika semuanya sudah terhubung ke konektor, itu tidak berfungsi. Artinya, penganalisa perangkat lunak tidak dapat mengatasinya di sini. Ketika saya sedang menulis, saya entah bagaimana terbawa suasana dan menulis satu blok berisi lima artikel. Sekarang kita dapat mengatakan bahwa mereka tidak hanya menunjukkan alat analisa itu sendiri, tetapi juga proses khas pembuatannya dalam mode "tergesa-gesa". Artikel ini akan menunjukkan kepada Anda bagaimana membuat analisa seperti itu tidak hanya berdasarkan Redd, tetapi juga pada papan tempat roti jadi,yang dapat dibeli di Ali Express.









Mungkin, hari ini saya bahkan akan mematahkan tradisi dan akan men-debug proyek bukan pada kompleks Redd, tetapi pada tata letak yang teratur. Pertama, saya sadar bahwa sebagian besar pembaca tidak memiliki akses ke kompleks seperti itu, tetapi mereka memiliki akses ke Ali Express. Nah, dan kedua, saya terlalu malas untuk memagari taman dengan sepasang perangkat USB dan host yang terhubung, dan juga untuk menangani gangguan yang muncul.



Kembali pada tahun 2017, saya mencari solusi yang sudah jadi di jaringan dan menemukan hal yang luar biasa , atau lebih tepatnya, leluhurnya. Sekarang mereka memiliki semua yang ada di papan khusus, tetapi di mana-mana ada foto-foto papan tempat memotong roti sederhana dari Xilinx, yang terhubung dengan papan dari WaveShare (Anda dapat mempelajarinya di sini ). Mari kita lihat foto board ini.







Ini memiliki dua konektor USB sekaligus. Selain itu, diagram menunjukkan bahwa mereka diparalelkan. Anda dapat menyambungkan perangkat USB Anda ke soket tipe A, dan Anda dapat menghubungkan kabel ke konektor mini USB, yang akan kami tancapkan ke host. Dan deskripsi proyek OpenVizsla mengatakan bahwa cara ini berfungsi. Sayangnya, proyek itu sendiri agak sulit dibaca. Anda dapat mengambilnya di github, tetapi saya akan memberikan tautan bukan ke akun yang ditunjukkan pada halaman, semua orang akan menemukannya, tetapi telah diulang untuk MiGen, tetapi versi yang saya temukan pada 2017: http: // github. com / ultraembedded / core, ada di Verilog yang bersih, dan ada cabang usb_sniffer. Di sana, semuanya berjalan tidak langsung melalui ULPI, tetapi melalui konverter ULPI ke UTMI (kedua kata-kata cabul ini adalah rangkaian mikro tingkat fisik yang cocok dengan saluran USB 2.0 berkecepatan tinggi dengan bus yang dapat dimengerti oleh prosesor dan FPGA), dan baru kemudian bekerja dengan UTMI ini. Bagaimana semuanya bekerja di sana, saya belum menemukan. Karena itu, saya lebih suka membuat pengembangan dari awal, karena kita akan segera melihat bahwa semuanya menakutkan di sana daripada sulit.



Perangkat keras apa yang bisa Anda gunakan



Jawaban atas pertanyaan dari judul itu sederhana: pada siapa pun yang memiliki FPGA dan memori eksternal. Tentu saja, dalam seri ini kami hanya akan mempertimbangkan Altera FPGA (Intel). Namun, perlu diingat bahwa data dari sirkuit mikro ULPI (ada di sapu tangan itu) berjalan pada 60 MHz. Kabel panjang tidak dapat diterima di sini. Penting juga untuk menghubungkan jalur CLK ke input FPGA dari grup GCK, jika tidak semuanya akan berfungsi dan kemudian gagal. Lebih baik tidak mengambil risiko. Saya tidak menyarankan Anda untuk meneruskannya secara terprogram. Saya mencoba. Semuanya berakhir dengan kawat ke kaki dari kelompok GCK.



Untuk percobaan hari ini, atas permintaan saya, seorang kenalan menyolder kepada saya sistem seperti itu:







Micromodule dengan FPGA dan SDRAM (lihat di ALI yang diungkapkan dengan frasa FPGA AC608) dan papan ULPI yang sama dari WaveShare. Beginilah tampilan modul di foto dari salah satu penjual. Aku terlalu malas untuk melepaskannya dari kasing:







Omong-omong, lubang ventilasi, seperti dalam foto kasingku, sangat menarik. Pada model, gambarkan layer solid, dan pada slicer atur fill, katakanlah, 40% dan katakan bahwa Anda perlu membuat nol layer solid dari bawah dan atas. Akibatnya, printer 3D menarik ventilasi ini sendiri. Sangat nyaman.

Secara umum, pendekatan untuk menemukan perangkat keras jelas. Sekarang kita mulai mendesain alat analisa. Alih-alih, kami telah membuat alat analisa itu sendiri dalam dua artikel terakhir (di sini kami bekerja dengan perangkat keras , dan di sini - dengan akses ke sana ), sekarang kami hanya akan merancang kepala berorientasi masalah yang menangkap data yang berasal dari sirkuit mikro ULPI.



Apa yang harus bisa dilakukan kepala



Dalam kasus penganalisa logika, semuanya mudah dan sederhana. Ada data. Kami terhubung dengan mereka dan mulai berkemas, dan mengirimkannya ke bus AVALON_ST. Semuanya lebih rumit di sini. Spesifikasi ULPI dapat ditemukan di sini . Sembilan puluh tiga lembar teks membosankan. Secara pribadi, ini membuat saya putus asa. Deskripsi untuk chip USB3300, yang dipasang di papan WaveShare, terlihat sedikit lebih sederhana. Anda bisa mendapatkannya di sini . Meskipun saya masih mengumpulkan keberanian sejak Desember 2017 itu, terkadang membaca dokumen dan segera menutupnya, karena saya merasakan pendekatan depresi.



Dari uraian itu jelas bahwa ULPI memiliki satu set register yang harus diisi sebelum mulai bekerja. Ini terutama karena resistor pull-up dan terminasi. Berikut ini adalah gambar untuk menjelaskan maksudnya:







Bergantung pada peran (host atau perangkat), serta kecepatan yang dipilih, resistor yang berbeda harus disertakan. Tapi kami bukan host atau perangkat! Kita harus memutus semua resistor agar tidak mengganggu perangkat utama di bus! Ini dilakukan dengan menulis ke register.



Nah, dan kecepatan. Perlu untuk memilih kecepatan kerja. Untuk melakukan ini, Anda juga perlu menulis ke register.



Ketika semuanya sudah terkonfigurasi, Anda dapat mulai mengambil data. Namun atas nama ULPI, huruf "LP" berarti "Pin Rendah". Dan pengurangan jumlah kaki ini menyebabkan protokol yang sangat marah yang hanya bertahan! Mari kita lihat lebih dekat protokolnya.



Protokol ULPI



Protokol ULPI agak tidak biasa bagi orang awam. Tetapi jika Anda duduk dengan dokumen dan bermeditasi, maka beberapa fitur yang kurang lebih dapat dipahami mulai muncul. Menjadi jelas bahwa pengembang telah melakukan segala upaya untuk benar-benar mengurangi jumlah kontak yang digunakan.



Saya tidak akan mengetik ulang dokumentasi lengkap di sini. Mari kita membatasi diri pada hal-hal yang paling penting. Yang paling penting dari ini adalah arah sinyal. Tidak mungkin untuk mengingatnya, lebih baik untuk melihat gambar setiap saat:







ULPI LINK adalah FPGA kami.



Diagram waktu penerimaan data



Saat istirahat, kita harus mengeluarkan 0x00 yang konstan ke bus data, yang sesuai dengan perintah IDLE. Jika data berasal dari bus USB, protokol pertukaran akan terlihat seperti ini:







Siklus akan dimulai dengan fakta bahwa sinyal DIR akan terbang hingga satu. Pertama, akan ada satu siklus clock sehingga sistem memiliki waktu untuk mengubah arah bus data. Selanjutnya - keajaiban ekonomi dimulai. Lihat nama sinyal NXT? Itu berarti BERIKUTNYA ketika dikirim dari kami. Dan ini dia sinyal yang sama sekali berbeda. Ketika DIR adalah satu, saya akan memanggil NXT C / D. Level rendah - kami memiliki tim. Data tinggi.



Artinya, kita harus memperbaiki 9 bit (bus DATA dan sinyal NXT) baik selalu pada DIR tinggi (kemudian memfilter jam pertama dengan perangkat lunak), atau mulai dari jam kedua setelah DIR lepas landas. Jika saluran DIR turun ke nol, kami mengganti bus data untuk menulis dan mulai lagi menyiarkan perintah IDLE.



Dengan penerimaan data - jelas. Sekarang mari kita menganalisis pekerjaan dengan register.



Diagram waktu penulisan untuk register ULPI



Untuk menulis ke register, rumah sementara berikut digunakan (saya sengaja beralih ke jargon, karena saya merasa saya cenderung ke GOST 2.105, dan ini membosankan, jadi saya akan menjauh darinya):







Pertama-tama, kita harus menunggu keadaan DIR = 0. Pada jam T0, kita harus mengatur TXD CMD konstan pada bus data. Apa artinya? Anda tidak dapat langsung mengetahuinya, tetapi jika Anda menggali sedikit melalui dokumen, ternyata nilai yang diinginkan dapat ditemukan di sini:







Yaitu, bit data tinggi harus ditetapkan ke nilai "10" (untuk seluruh byte, mask adalah 0x80), dan yang lebih rendah - nomor register.



Selanjutnya, Anda harus menunggu sinyal NXT lepas landas. Dengan sinyal ini, microcircuit mengkonfirmasi bahwa ia mendengar kami. Pada gambar di atas, kami menunggu di jam T2 dan mengatur data pada jam berikutnya (T3). Pada jam T4, ULPI akan menerima data dan menghapus NXT. Dan kami akan menandai akhir dari siklus pertukaran unit di STP. Pada T5 juga, data akan dimasukkan ke dalam register internal. Prosesnya telah berakhir. Berikut adalah pengembalian untuk sejumlah kecil kesimpulan. Tetapi kita harus menulis data hanya pada saat startup, jadi, tentu saja, kita harus menderita dengan pengembangan, tetapi semua ini tidak akan mempengaruhi pekerjaan.



Diagram waktu pembacaan dari register ULPI



Sejujurnya, untuk tugas-tugas praktis, membaca register tidak begitu penting, tetapi mari kita lihat juga. Membaca akan bermanfaat setidaknya untuk memastikan bahwa kami telah mengimplementasikan catatan dengan benar.







Kita melihat bahwa di hadapan kita adalah campuran eksplosif dari dua rumah sementara sebelumnya. Kami menetapkan alamat seperti yang kami lakukan untuk menulis ke register, dan kami mengambil data sesuai dengan aturan untuk membaca data.



Baik? Mari kita mulai merancang otomat yang akan membentuk semua ini untuk kita?



Diagram struktural kepala



Seperti yang dapat Anda lihat dari uraian di atas, head harus terhubung ke dua bus sekaligus: AVALON_MM untuk mengakses register dan AVALON_ST untuk mengirim data agar disimpan dalam RAM. Hal utama di kepala adalah otak. Dan itu harus menjadi mesin negara yang akan menghasilkan diagram waktu yang kita bahas sebelumnya.







Mari kita mulai pengembangannya dengan fungsi menerima data. Harus diingat di sini bahwa kita tidak dapat mempengaruhi aliran dari bus ULPI dengan cara apa pun. Data dari sana, jika mulai pergi, itu akan pergi. Mereka tidak peduli apakah AVALON_ST bus siap atau tidak. Karena itu, kami hanya akan mengabaikan ketidaktersediaan bus. Dalam penganalisa nyata, akan dimungkinkan untuk menambahkan indikasi alarm jika output data tanpa kesiapan. Semuanya harus sederhana dalam kerangka artikel, jadi mari kita ingat ini untuk masa depan. Dan untuk memastikan ketersediaan bus, seperti dalam penganalisa logika, kita akan memiliki blok FIFO eksternal. Secara total, grafik transisi otomat untuk menerima aliran data adalah sebagai berikut:







DIR lepas landas - mulai menerima. Kami menggantung satu jam di wait1, lalu kami menerimanya sementara DIR sama dengan satu. Jatuh ke nol - setelah satu jam (meskipun bukan fakta bahwa itu diperlukan, tetapi untuk sekarang kita akan menetapkan status wait2) kembali ke siaga.



Sejauh ini, semuanya sederhana. Jangan lupa bahwa tidak hanya jalur D0_D7, tetapi juga jalur NXT harus pergi ke bus AVALON_ST, karena itu menentukan apa yang sedang dikirim sekarang: perintah atau data.



Siklus register register dapat memiliki waktu eksekusi yang tidak dapat diprediksi. Dari sudut pandang bus AVALON_MM, ini tidak terlalu bagus. Karena itu, kami akan membuatnya sedikit lebih rumit. Mari kita buat register buffer. Data akan masuk ke dalamnya, setelah itu AVALON_MM bus akan segera dirilis. Dari sudut pandang otomat yang sedang dikembangkan, sinyal input have_reg muncul (data dalam register telah diterima, yang harus dikirim) dan sinyal output yang diregister (artinya proses penerbitan register telah selesai). Tambahkan logika penulisan ke register pada grafik transisi otomat.







Saya telah menyoroti kondisi DIR = 1 berwarna merah untuk menjelaskan bahwa ini memiliki prioritas tertinggi. Maka dimungkinkan untuk mengesampingkan ekspektasi nilai nol dari sinyal DIR di cabang otomat yang baru. Masuk ke cabang dengan nilai yang berbeda tidak akan mungkin dilakukan. Status SET_CMDw berwarna biru karena sangat mungkin virtual. Ini hanya tindakan yang harus dilakukan! Tidak ada yang mengganggu untuk menetapkan konstanta yang sesuai pada bus data dan hanya selama transisi! Dalam keadaan STPw, antara lain, sinyal reg_served juga dapat dikokang untuk satu siklus clock untuk menghapus sinyal BSY untuk bus AVALON_MM, yang memungkinkan siklus penulisan baru.



Yah, tetap menambahkan cabang untuk membaca register ULPI. Di sini, yang terjadi adalah sebaliknya. Mesin layanan bus mengirimi kami permintaan dan menunggu respons kami. Ketika data diterima, ia bisa memprosesnya. Dan itu akan bekerja dengan suspensi bus atau pemungutan suara, ini sudah menjadi masalah mesin itu. Hari ini saya memutuskan untuk mengerjakan survei. Meminta data - BSY muncul. Bagaimana BSY menghilang - Anda dapat menerima data baca. Secara total, grafik mengambil bentuk:







Mungkin, dalam perjalanan pengembangan, akan ada beberapa penyesuaian, tetapi untuk sekarang, kami akan mematuhi grafik ini. Bagaimanapun, ini bukan laporan, tetapi instruksi tentang metodologi pengembangan. Dan tekniknya sedemikian rupa sehingga pertama-tama Anda perlu menggambar grafik transisi, dan kemudian - lakukan logika, sesuai dengan gambar ini, disesuaikan dengan detail pop-up.



Fitur implementasi otomat dari sisi AVALON_MM



Saat bekerja dengan AVALON_MM bus, Anda bisa menggunakan dua cara. Yang pertama adalah membuat penundaan akses bus. Kami menjelajahi mekanisme ini di salah satu artikel sebelumnya , dan saya memperingatkan bahwa itu penuh dengan masalah. Cara kedua adalah klasik. Masukkan register status. Di awal transaksi, atur sinyal BSY, pada penyelesaiannya - reset. Dan berikan tanggung jawab untuk semuanya ke logika master bus (prosesor Nios II atau jembatan JTAG). Masing-masing opsi memiliki kelebihan dan kekurangannya sendiri. Karena kita telah melakukan varian dengan penundaan bus, mari kita lakukan semuanya hari ini, untuk perubahan, melalui register status.



Kami merancang mesin utama



Hal pertama yang saya ingin menarik perhatian Anda adalah pemicu RS favorit saya. Kami memiliki dua mesin. Yang pertama melayani bus AVALON_MM, yang kedua - antarmuka ULPI. Kami menemukan bahwa koneksi di antara mereka melewati beberapa bendera. Hanya satu proses yang dapat menulis ke setiap bendera. Setiap otomat diimplementasikan oleh prosesnya sendiri. Bagaimana menjadi Untuk beberapa waktu sekarang, saya baru saja mulai menambahkan pemicu RS. Kami memiliki dua bit, sehingga harus dihasilkan oleh dua sandal jepit RS. Di sini mereka:

//   
always_ff @(posedge ulpi_clk)
begin
      //    
      if  (reg_served)
           write_busy <= 0;
      else if (have_reg)
           write_busy <= 1;

      //    
      if  (read_finished)
           read_busy <= 0;
      else if (reg_request)
           read_busy <= 1;
end


Satu proses cocks reg_served, yang kedua cocks have_reg. Dan RS-flip-flop dalam prosesnya sendiri menghasilkan sinyal write_busy atas dasar mereka. Demikian pula, read_busy dibentuk dari read_finished dan reg_request. Anda dapat melakukannya secara berbeda, tetapi pada tahap jalur kreatif ini, saya menyukai metode ini.



Ini adalah bagaimana flag BSY diatur. Kuning untuk proses penulisan, biru untuk proses membaca. Proses Verilogov memiliki satu fitur yang sangat menarik. Di dalamnya, Anda dapat menetapkan nilai tidak hanya sekali, tetapi beberapa kali. Oleh karena itu, jika saya ingin sinyal lepas landas untuk satu siklus clock, saya membatalkannya di awal proses (kita melihat bahwa kedua sinyal dibatalkan di sana), dan mengaturnya menjadi satu dengan kondisi yang dijalankan selama satu siklus clock. Memasuki kondisi akan menimpa default. Dalam semua kasus lain, ini akan berhasil. Jadi, menulis ke port data akan memulai takeoff dari sinyal have_reg untuk satu siklus clock, dan menulis bit 0 ke port kontrol akan memulai takeoff dari sinyal reg_request.





Teks yang sama.
//  AVALON_MM  
always_ff @(posedge ulpi_clk)
begin
   //    ,    
   //      
   have_reg    <= 0;
   reg_request <= 0;

   if (write == 1) 
   begin
      case (address)
          0 : addr_to_ulpi <= writedata [5:0];
          //       
          1 : begin
                data_to_ulpi <= writedata [7:0];
                have_reg <= 1;
              end
          2 : begin
                //      
                reg_request <= writedata[0];
		force_reset = writedata [31];
              end
         3: begin end
      endcase
   end
end   






Seperti yang kita lihat di atas, satu siklus clock sudah cukup untuk flip-flop RS terkait untuk diatur ke satu. Dan mulai saat ini, sinyal BSY yang ditetapkan mulai dibaca dari register status:





Teks yang sama.
//  AVALON_MM  
always_comb 
begin
   case (address)
      //   (  )
      0 : readdata <= {26'b0, addr_to_ulpi};

      //  
      1 : readdata <= {23'b0, data_from_ulpi};

      // 2 -  ,   -   

      //  
      3 : readdata <= {30'b0, (reg_request | read_busy), (have_reg | write_busy)};
      default: readdata <= 0;
   endcase
end   






Sebenarnya, secara alami kami berkenalan dengan proses yang melayani pekerjaan dengan bus AVALON_MM.

Izinkan saya juga mengingatkan Anda tentang prinsip-prinsip bekerja dengan bus ulpi_data. Bus ini dua arah. Karena itu, Anda harus menggunakan teknik standar untuk bekerja dengannya. Ini adalah bagaimana port yang sesuai dinyatakan:

   inout        [7:0]  ulpi_data,


Kita dapat membaca dari bus ini, tetapi kita tidak dapat menulis secara langsung. Sebagai gantinya, kami membuat salinan untuk catatan.

logic [7:0] ulpi_d = 0;


Dan kami menghubungkan salinan ini ke bus utama melalui multiplexer berikut:

//      inout-
assign ulpi_data = (ulpi_dir == 0) ? ulpi_d : 8'hzz;


Saya mencoba mengomentari logika mesin utama sebanyak mungkin di dalam kode Verilog. Seperti yang saya harapkan selama pengembangan grafik transisi, dalam implementasi nyata, logikanya agak berubah. Beberapa negara bagian diusir. Meskipun demikian, dengan membandingkan grafik dan teks sumber, saya harap Anda memahami semua yang dilakukan di sana. Karena itu, saya tidak akan membicarakan mesin ini. Lebih baik memberikan referensi teks lengkap modul, yang relevan pada saat sebelum modifikasi berdasarkan hasil percobaan praktis.

Teks lengkap modul.
module ULPIhead
(
   input               reset,
   output              clk66,

   // AVALON_MM
   input        [1:0]  address,
   input               write,
   input        [31:0] writedata,
   input               read,
   output logic [31:0] readdata = 0,

   // AVALON_ST
   input  logic        source_ready,
   output logic        source_valid = 0,
   output logic [15:0] source_data = 0,

   // ULPI
   inout        [7:0]  ulpi_data,
   output logic        ulpi_stp = 0,
   input               ulpi_nxt,
   input               ulpi_dir,
   input               ulpi_clk,
   output              ulpi_rst
);

logic      have_reg = 0;
logic      reg_served = 0;
logic      reg_request = 0;
logic      read_finished = 0;
logic [5:0] addr_to_ulpi;
logic [7:0] data_to_ulpi;
logic [7:0] data_from_ulpi;

logic      write_busy = 0;
logic      read_busy = 0;

logic [7:0] ulpi_d = 0;

logic force_reset = 0;

//   
always_ff @(posedge ulpi_clk)
begin
      //    
      if  (reg_served)
           write_busy <= 0;
      else if (have_reg)
           write_busy <= 1;

      //    
      if  (read_finished)
           read_busy <= 0;
      else if (reg_request)
           read_busy <= 1;
end

//  AVALON_MM  
always_comb 
begin
   case (address)
      //   (  )
      0 : readdata <= {26'b0, addr_to_ulpi};

      //  
      1 : readdata <= {23'b0, data_from_ulpi};

      // 2 -  ,   -   

      //  
      3 : readdata <= {30'b0, (reg_request | read_busy), (have_reg | write_busy)};
      default: readdata <= 0;
   endcase
end   

//  AVALON_MM  
always_ff @(posedge ulpi_clk)
begin
   //    ,    
   //      
   have_reg    <= 0;
   reg_request <= 0;

   if (write == 1) 
   begin
      case (address)
          0 : addr_to_ulpi <= writedata [5:0];
          //       
          1 : begin
                data_to_ulpi <= writedata [7:0];
                have_reg <= 1;
              end
          2 : begin
                //      
                reg_request <= writedata[0];
		force_reset = writedata [31];
              end
         3: begin end
      endcase
   end
end   

//   
enum {idle,
wait1,wr_st,
wait_nxt_w,hold_w,
wait_nxt_r,wait_dir1,latch,wait_dir0

} state = idle;
always_ff @ (posedge ulpi_clk)
begin
   if (reset)
   begin
       state <= idle;
   end else
   begin
      //    
      source_valid <= 0;
      reg_served  <= 0;
      ulpi_stp <= 0;
      read_finished <= 0;
      case (state)
      idle: begin
           if (ulpi_dir)
               state <= wait1;
           else if (have_reg) 
                begin
                  //      , 
                  //    ,   
                  // 
                  ulpi_d [7:6] <= 2'b10;
                  ulpi_d [5:0] <= addr_to_ulpi;
                  state <= wait_nxt_w;
                end
           else if (reg_request)
                begin
                  //  -   
                  ulpi_d [7:6] <= 2'b11;
                  ulpi_d [5:0] <= addr_to_ulpi;
                  state <= wait_nxt_r;
                end
         end
      //      TURN_AROUND
      wait1 : begin
            state <= wr_st;
            //    ,   
            source_valid <= 1; 
            source_data <= {7'h0,!ulpi_nxt,ulpi_data};
         end
      //     DIR -    AVALON_ST
      wr_st : begin
            if (ulpi_dir)
            begin
              //   ,    
               source_valid <= 1;
               source_data <= {7'h0,!ulpi_nxt,ulpi_data};
            end else
               //      wait2,
               //   ,   - . 
               state <= idle;
         end
      wait_nxt_w : begin
           if (ulpi_nxt)
           begin
              ulpi_d <= data_to_ulpi;
              state <= hold_w;
           end
         end
      hold_w: begin
           //   ,  ULPI 
           //     .   NXT
           //  ...
           if (ulpi_nxt) begin
              // ,  AVALON_MM    
              reg_served  <= 1;
              ulpi_d <= 0;    //   idle
              ulpi_stp <= 1;  //     STP
              state <= idle;  //   -    idle
           end
         end
       //   STPw   ...
       // ...
      //    . ,   NXT
      //    ,    
      wait_nxt_r : begin
           if (ulpi_nxt)
           begin
              ulpi_d <= 0;    //    
              state <= wait_dir1;
           end
         end
      // ,    
      wait_dir1: begin
          if (ulpi_dir)
             state <= latch;
        end
      //    
      //   -   
      latch: begin
          data_from_ulpi <= ulpi_data;
          state <= wait_dir0;
        end
      // ,     
      wait_dir0: begin
          if (!ulpi_dir)
          begin
             state <= idle;
             read_finished <= 1;
          end
        end
   
      default:	begin
         state <= idle;
         end
      endcase
    end
end
//      inout-
assign ulpi_data = (ulpi_dir == 0) ? ulpi_d : 8'hzz;

// reset   ,      
assign ulpi_rst = reset | force_reset;

assign clk66 = ulpi_clk;

endmodule




Panduan Programmer



Port alamat register ULPI (+0)



Alamat register bus ULPI harus ditempatkan di port dengan offset +0, yang akan digunakan



Port Data Registrasi ULPI (+4)



Saat menulis ke port ini: proses penulisan ke register ULPI, alamat yang ditetapkan di port alamat register, dimulai secara otomatis. Dilarang menulis ke port ini sampai proses penulisan sebelumnya selesai.



Sedang Dibaca: Port ini akan mengembalikan nilai yang diperoleh dari pembacaan terakhir dari register ULPI.



Port kontrol ULPI (+8)



Bacaan selalu nol. Penugasan bit untuk penulisan adalah sebagai berikut:



Bit 0 - Saat menulis nilai tunggal, memulai proses membaca register ULPI, alamatnya diatur di port alamat register ULPI.



Bit 31 - Saat menulis satu, kirim sinyal RESET ke chip ULPI.



Sisa bit disediakan.



Port status (+ 0x0C)



Hanya baca.



Bit 0 - WRITE_BUSY. Jika sama dengan satu, proses penulisan ke register ULPI sedang berlangsung.



Bit 1 - READ_BUSY. Jika sama dengan satu, proses membaca dari register ULPI sedang berlangsung.



Sisa bit disediakan.



Kesimpulan



Kami berkenalan dengan metode organisasi fisik kepala penganalisa USB, merancang otomat dasar untuk bekerja dengan mikrokircuit ULPI dan menerapkan modul SystemVerilog rancangan untuk kepala ini. Dalam artikel selanjutnya, kita akan melihat proses pemodelan, mensimulasikan modul ini, dan kemudian melakukan eksperimen praktis dengannya, berdasarkan pada hasil yang kita akan menyelesaikan kode dengan bersih. Begitulah, hingga akhirnya kami memiliki setidaknya empat artikel lagi.



All Articles