Визуальная электроника

Те́трис (производное от «тетрамино» и «теннис»[2]) — компьютерная игра, первоначально изобретённая и разработанная советским программистом Алексеем Пажитновым. Игра была выпущена 6 июня 1984 года — в это время Пажитнов работал в Вычислительном центре Академии наук СССР.

«Тетрис» представляет собой головоломку, построенную на использовании геометрических фигур «тетрамино» — разновидности полимино, состоящих из четырёх квадратов. Полимино в том или ином виде использовались в настольных играх и головоломках задолго до создания «Тетриса». Идею «Тетриса» Пажитнову подсказала игра в пентамино[1]. Первоначальная версия игры была написана Пажитновым на языке Паскаль[3][4] для компьютера «Электроника-60». Коммерческая версия игры — первая из многих последующих — была выпущена американской компанией Spectrum HoloByte (англ.) в 1987 году. В последующие годы «Тетрис» во множестве различных версий был портирован на великое множество устройств, включая все возможные компьютеры и игровые консоли, а также такие устройства, как графические калькуляторымобильные телефонымедиа-плеерыкарманные персональные компьютеры и — в качестве «пасхального яйца» — устройства, вовсе не предназначенные для воспроизведения медиа-контента, такие, как осциллограф и паяльник[5]. Ну а мы создадим реализацию для платы VE-EP4CE10E:

 

Но по-моему объяснения тут излишни, навряд ли найдется человек, который не знает, что такое Тетрис! Как только я увидел статью на habrahabr.ru, решил портировать проект на нашу плату VE-EP4CE10E.

Основной модуль написан на System Verilog, и представлен ниже:

SystemVerilog Code:
  1. `include "./tetris/rtl/defs.vh"
  2.  
  3. module top(
  4.  
  5. input CLOCK_50,
  6.  
  7. input [9:0] SW,
  8.  
  9.  
  10. ///////// PS2 /////////
  11. inout PS2_CLK,
  12. inout PS2_DAT,
  13.  
  14. ///////// VGA /////////
  15. output [7:0] VGA_B,
  16. output VGA_BLANK_N,
  17. output VGA_CLK,
  18. output [7:0] VGA_G,
  19. output VGA_HS,
  20. output [7:0] VGA_R,
  21. output VGA_SYNC_N, // not used (?)
  22. output VGA_VS
  23.  
  24. );
  25.  
  26. logic vga_clk;
  27.  
  28. pll pll1(
  29. .inclk0 (CLOCK_50),
  30. .c0 (),
  31. .c1 (vga_clk),
  32. .c2 (),
  33. .c3 ()
  34. );
  35.  
  36. assign VGA_CLK = vga_clk;
  37.  
  38. logic main_reset;
  39.  
  40. logic sw_0_d1;
  41. logic sw_0_d2;
  42. logic sw_0_d3;
  43.  
  44. always_ff @( posedge CLOCK_50 )
  45. begin
  46. sw_0_d1 <= SW[0];
  47. sw_0_d2 <= sw_0_d1;
  48. sw_0_d3 <= sw_0_d2;
  49. end
  50.  
  51. assign main_reset = sw_0_d3;
  52.  
  53. logic [7:0] ps2_received_data_w;
  54. logic ps2_received_data_en_w;
  55.  
  56. PS2_Controller ps2(
  57. .CLOCK_50 ( CLOCK_50 ),
  58. .reset ( main_reset ),
  59.  
  60. // Bidirectionals
  61. .PS2_CLK ( PS2_CLK ),
  62. .PS2_DAT ( PS2_DAT ),
  63.  
  64. .received_data ( ps2_received_data_w ),
  65. .received_data_en ( ps2_received_data_en_w )
  66. );
  67.  
  68. logic user_event_rd_req_w;
  69. user_event_t user_event_w;
  70. logic user_event_ready_w;
  71.  
  72. user_input user_input(
  73. .rst_i ( main_reset ),
  74.  
  75. .ps2_clk_i ( CLOCK_50 ),
  76.  
  77. .ps2_key_data_i ( ps2_received_data_w ),
  78. .ps2_key_data_en_i ( ps2_received_data_en_w ),
  79.  
  80. .main_logic_clk_i ( vga_clk ),
  81.  
  82. .user_event_rd_req_i ( user_event_rd_req_w ),
  83. .user_event_o ( user_event_w ),
  84. .user_event_ready_o ( user_event_ready_w )
  85.  
  86. );
  87.  
  88. game_data_t game_data_w;
  89.  
  90. main_game_logic main_logic(
  91.  
  92. .clk_i ( vga_clk ),
  93. .rst_i ( main_reset ),
  94.  
  95. .user_event_i ( user_event_w ),
  96. .user_event_ready_i ( user_event_ready_w ),
  97. .user_event_rd_req_o ( user_event_rd_req_w ),
  98.  
  99. .game_data_o ( game_data_w )
  100.  
  101. );
  102.  
  103. draw_tetris draw_tetris(
  104.  
  105. .clk_vga_i ( vga_clk ),
  106.  
  107. .game_data_i ( game_data_w ),
  108.  
  109. // VGA interface
  110. .vga_hs_o ( VGA_HS ),
  111. .vga_vs_o ( VGA_VS ),
  112. .vga_de_o ( VGA_BLANK_N ),
  113. .vga_r_o ( VGA_R ),
  114. .vga_g_o ( VGA_G ),
  115. .vga_b_o ( VGA_B )
  116.  
  117. );
  118.  
  119. endmodule

FSM «общается» со следующими блоками/модулями:

  • gen_sys_event — таймер, который отсчитывает время, через которое надо автоматически двинуть фигурку вниз.
  • gen_next_block — генератор новой фигурки.
  • check_move — проверка, можно ли выполнить текущий «ход».
  • tetris_stat — накопление «статистики».
  • user_input — считывает событие, которое «произвел» пользователь.

Всё очень похоже на «обычную» реализацию Тетриса, которые написаны на C++/Java/etc: различные модули выполняют роль функций в тех языках. Да и проблемы возникают такие же: дольше всего сидел над переворотом фигурки, ответ подсмотрел в коде quadrapassel. Один из вариантов является то, что можно хранить хранить таблицу всех возможных разворотов (для каждой фигурки четыре варианта).

Весь код написан на Verilog, а если быть более точным — на SystemVerilog. С одной стороны SystemVerilog намного гибче, чем Verilog, а с другой стороны это приводит к тому, что ты не ограничен, и хочешь всё больше и больше различных рюшечек реализовать :).

В принципе в оригинальной статье логика работы описана неплохо, поэтому мы ограничимся видео работы, и исходными кодами для нашей платы.

Проект игры Tetris: yafpgatetris.zip

Добавить комментарий