vid_top.v 7.6 KB

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