vid_top.v 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /*
  2. * vid_top.v
  3. *
  4. * Top-level for the video module
  5. *
  6. * vim: ts=4 sw=4
  7. *
  8. * Copyright (C) 2021 Sylvain Munaut <tnt@246tNt.com>
  9. * SPDX-License-Identifier: CERN-OHL-P-2.0
  10. */
  11. `default_nettype none
  12. /* Use 640x480 60 Hz video and not original 640x400 70 Hz mode */
  13. `define COMPAT_MODE
  14. module vid_top (
  15. // Video output
  16. output wire [3:0] hdmi_r,
  17. output wire [3:0] hdmi_g,
  18. output wire [3:0] hdmi_b,
  19. output wire hdmi_hsync,
  20. output wire hdmi_vsync,
  21. output wire hdmi_de,
  22. output wire hdmi_clk,
  23. // Wishbone
  24. input wire [15:0] wb_addr,
  25. output reg [31:0] wb_rdata,
  26. input wire [31:0] wb_wdata,
  27. input wire [ 3:0] wb_wmsk,
  28. input wire wb_we,
  29. input wire wb_cyc,
  30. output reg wb_ack,
  31. // Clock / Reset
  32. input wire clk,
  33. input wire rst
  34. );
  35. // Signals
  36. // -------
  37. // Frame Buffer
  38. wire [13:0] fb_v_addr_0;
  39. wire [31:0] fb_v_data_1;
  40. wire fb_v_re_0;
  41. wire [13:0] fb_a_addr_0;
  42. wire [31:0] fb_a_rdata_1;
  43. wire [31:0] fb_a_wdata_0;
  44. wire [ 3:0] fb_a_wmsk_0;
  45. wire fb_a_we_0;
  46. wire fb_a_rdy_0;
  47. // Palette
  48. wire [ 7:0] pal_w_addr;
  49. wire [15:0] pal_w_data;
  50. wire pal_w_ena;
  51. wire [ 7:0] pal_r_addr_0;
  52. wire [15:0] pal_r_data_1;
  53. // Timing gen
  54. wire tg_hsync_0;
  55. wire tg_vsync_0;
  56. wire tg_active_0;
  57. wire tg_h_first_0;
  58. wire tg_h_last_0;
  59. wire tg_v_first_0;
  60. wire tg_v_last_0;
  61. // Video status
  62. reg [15:0] vs_frame_cnt;
  63. reg vs_in_vbl;
  64. // Pixel pipeline
  65. reg pp_active_1;
  66. reg pp_ydbl_1;
  67. reg pp_xdbl_1;
  68. reg [15:0] pp_addr_base_1;
  69. reg [15:0] pp_addr_cur_1;
  70. reg pp_data_load_2;
  71. reg [31:0] pp_data_3;
  72. wire [11:0] pp_data_4;
  73. wire pp_hsync_4;
  74. wire pp_vsync_4;
  75. wire pp_de_4;
  76. // Frame Buffer
  77. // ------------
  78. vid_framebuf fb_I (
  79. .v_addr_0 (fb_v_addr_0),
  80. .v_data_1 (fb_v_data_1),
  81. .v_re_0 (fb_v_re_0),
  82. .a_addr_0 (fb_a_addr_0),
  83. .a_rdata_1 (fb_a_rdata_1),
  84. .a_wdata_0 (fb_a_wdata_0),
  85. .a_wmsk_0 (fb_a_wmsk_0),
  86. .a_we_0 (fb_a_we_0),
  87. .a_rdy_0 (fb_a_rdy_0),
  88. .clk (clk)
  89. );
  90. // Palette
  91. // -------
  92. vid_palette pal_I (
  93. .w_addr_0 (pal_w_addr),
  94. .w_data_0 (pal_w_data),
  95. .w_ena_0 (pal_w_ena),
  96. .r_addr_0 (pal_r_addr_0),
  97. .r_data_1 (pal_r_data_1),
  98. .clk (clk)
  99. );
  100. // Timing Generator
  101. // ----------------
  102. vid_tgen #(
  103. `ifndef COMPAT_MODE
  104. .H_WIDTH ( 10 ),
  105. .H_FP ( 16 ),
  106. .H_SYNC ( 96 ),
  107. .H_BP ( 48 ),
  108. .H_ACTIVE ( 640 ),
  109. .V_WIDTH ( 9 ),
  110. .V_FP ( 12 ),
  111. .V_SYNC ( 2 ),
  112. .V_BP ( 35 ),
  113. .V_ACTIVE ( 400 )
  114. `else
  115. .H_WIDTH ( 10 ),
  116. .H_FP ( 16 ),
  117. .H_SYNC ( 96 ),
  118. .H_BP ( 48 ),
  119. .H_ACTIVE ( 640 ),
  120. .V_WIDTH ( 9 ),
  121. .V_FP ( 10 ),
  122. .V_SYNC ( 2 ),
  123. .V_BP ( 33 ),
  124. .V_ACTIVE ( 480 )
  125. `endif
  126. ) tgen_I (
  127. .vid_hsync (tg_hsync_0),
  128. .vid_vsync (tg_vsync_0),
  129. .vid_active (tg_active_0),
  130. .vid_h_first (tg_h_first_0),
  131. .vid_h_last (tg_h_last_0),
  132. .vid_v_first (tg_v_first_0),
  133. .vid_v_last (tg_v_last_0),
  134. .clk (clk),
  135. .rst (rst)
  136. );
  137. // Video Status and counter
  138. // ------------------------
  139. always @(posedge clk)
  140. vs_in_vbl <= (vs_in_vbl & ~tg_v_first_0) | (tg_v_last_0 & tg_h_last_0);
  141. always @(posedge clk)
  142. if (rst)
  143. vs_frame_cnt <= 0;
  144. else
  145. vs_frame_cnt <= vs_frame_cnt + (tg_v_last_0 & tg_h_last_0);
  146. // Video Pipeline
  147. // --------------
  148. // Pixel fetch
  149. `ifndef COMPAT_MODE
  150. // Counter control in 640x400 -> Double pixels
  151. always @(posedge clk) begin
  152. pp_active_1 <= tg_active_0;
  153. pp_ydbl_1 <= (pp_ydbl_1 ^ tg_h_first_0) | tg_v_first_0;
  154. pp_xdbl_1 <= (pp_xdbl_1 ^ 1'b1 ) & ~tg_h_first_0;
  155. end
  156. `else
  157. // Counter control in 640x480 -> Double X, Double-or-Triple Y
  158. reg [3:0] pp_yscale_state;
  159. always @(posedge clk)
  160. if (tg_h_first_0) begin
  161. if (tg_v_first_0) begin
  162. pp_yscale_state <= 4'h0;
  163. pp_ydbl_1 <= 1'b0;
  164. end else begin
  165. case (pp_yscale_state)
  166. 4'h0: { pp_ydbl_1, pp_yscale_state } <= { 1'b1, 4'h1 };
  167. 4'h1: { pp_ydbl_1, pp_yscale_state } <= { 1'b0, 4'h2 };
  168. 4'h2: { pp_ydbl_1, pp_yscale_state } <= { 1'b1, 4'h3 };
  169. 4'h3: { pp_ydbl_1, pp_yscale_state } <= { 1'b0, 4'h4 };
  170. 4'h4: { pp_ydbl_1, pp_yscale_state } <= { 1'b0, 4'h5 };
  171. 4'h5: { pp_ydbl_1, pp_yscale_state } <= { 1'b1, 4'h6 };
  172. 4'h6: { pp_ydbl_1, pp_yscale_state } <= { 1'b0, 4'h7 };
  173. 4'h7: { pp_ydbl_1, pp_yscale_state } <= { 1'b1, 4'h8 };
  174. 4'h8: { pp_ydbl_1, pp_yscale_state } <= { 1'b0, 4'h9 };
  175. 4'h9: { pp_ydbl_1, pp_yscale_state } <= { 1'b0, 4'ha };
  176. 4'ha: { pp_ydbl_1, pp_yscale_state } <= { 1'b1, 4'hb };
  177. 4'hb: { pp_ydbl_1, pp_yscale_state } <= { 1'b0, 4'h0 };
  178. default: { pp_ydbl_1, pp_yscale_state } <= { 1'b0, 4'h0 };
  179. endcase;
  180. end
  181. end
  182. always @(posedge clk) begin
  183. pp_active_1 <= tg_active_0;
  184. pp_xdbl_1 <= (pp_xdbl_1 ^ 1'b1) & ~tg_h_first_0;
  185. end
  186. `endif
  187. // Counters
  188. always @(posedge clk)
  189. if (tg_h_first_0) begin
  190. if (tg_v_first_0)
  191. pp_addr_base_1 <= 0;
  192. else
  193. pp_addr_base_1 <= pp_addr_base_1 + (pp_ydbl_1 ? 16'd320 : 16'd0);
  194. end
  195. always @(posedge clk)
  196. if (tg_h_first_0)
  197. pp_addr_cur_1 <= tg_v_first_0 ? 16'd0 : pp_addr_base_1;
  198. else
  199. pp_addr_cur_1 <= pp_addr_cur_1 + pp_xdbl_1;
  200. // Frame Buffer
  201. assign fb_v_addr_0 = pp_addr_cur_1[15:2];
  202. assign fb_v_re_0 = pp_active_1 & (pp_addr_cur_1[1:0] == 2'b00) & ~pp_xdbl_1;
  203. // Shift Reg
  204. always @(posedge clk)
  205. pp_data_load_2 <= fb_v_re_0;
  206. always @(posedge clk)
  207. if (pp_xdbl_1)
  208. pp_data_3 <= pp_data_load_2 ? fb_v_data_1 : { 8'h00, pp_data_3[31:8] };
  209. // Palette fetch
  210. assign pal_r_addr_0 = pp_data_3[7:0];
  211. assign pp_data_4 = {
  212. pal_r_data_1[15:12], // R[15:11]
  213. pal_r_data_1[10: 7], // G[10: 5]
  214. pal_r_data_1[4:1] // B[ 4: 0]
  215. };
  216. // Sync signals
  217. delay_bit #(4) dly_hsync ( ~tg_hsync_0, pp_hsync_4, clk );
  218. delay_bit #(4) dly_vsync ( ~tg_vsync_0, pp_vsync_4, clk );
  219. delay_bit #(4) dly_de ( tg_active_0, pp_de_4, clk );
  220. // Output buffers
  221. hdmi_phy_1x #(
  222. .DW(12)
  223. ) phy_I (
  224. .hdmi_data ({hdmi_r, hdmi_g, hdmi_b}),
  225. .hdmi_hsync (hdmi_hsync),
  226. .hdmi_vsync (hdmi_vsync),
  227. .hdmi_de (hdmi_de),
  228. .hdmi_clk (hdmi_clk),
  229. .in_data (pp_data_4),
  230. .in_hsync (pp_hsync_4),
  231. .in_vsync (pp_vsync_4),
  232. .in_de (pp_de_4),
  233. .clk (clk)
  234. );
  235. // Bus Interface
  236. // -------------
  237. // Ack
  238. always @(posedge clk)
  239. wb_ack <= wb_cyc & ~wb_ack & (~wb_addr[15] | fb_a_rdy_0);
  240. // Read Mux
  241. always @(*)
  242. begin
  243. wb_rdata = 32'h00000000;
  244. if (wb_ack)
  245. wb_rdata = wb_addr[15] ? fb_a_rdata_1 : { 15'h0000, vs_in_vbl, vs_frame_cnt };
  246. end
  247. // Frame Buffer write
  248. assign fb_a_addr_0 = wb_addr[13:0];
  249. assign fb_a_wdata_0 = wb_wdata;
  250. assign fb_a_wmsk_0 = wb_wmsk;
  251. assign fb_a_we_0 = wb_cyc & wb_we & ~wb_ack & wb_addr[15];
  252. // Palette write
  253. assign pal_w_addr = wb_addr[7:0];
  254. assign pal_w_data = { wb_wdata[23:19], wb_wdata[15:10], wb_wdata[7:3] };
  255. assign pal_w_ena = wb_cyc & wb_we & ~wb_ack & (wb_addr[15:14] == 2'b01);
  256. endmodule // vid_top