hub75_framebuffer.v 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /*
  2. * hub75_framebuffer.v
  3. *
  4. * vim: ts=4 sw=4
  5. *
  6. * Copyright (C) 2019 Sylvain Munaut <tnt@246tNt.com>
  7. * All rights reserved.
  8. *
  9. * LGPL v3+, see LICENSE.lgpl3
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation; either
  14. * version 3 of the License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public License
  22. * along with this program; if not, write to the Free Software Foundation,
  23. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  24. */
  25. `default_nettype none
  26. module hub75_framebuffer #(
  27. parameter integer N_BANKS = 2,
  28. parameter integer N_ROWS = 32,
  29. parameter integer N_COLS = 64,
  30. parameter integer N_CHANS = 3,
  31. parameter integer N_PLANES = 8,
  32. parameter integer BITDEPTH = 24,
  33. // Auto-set
  34. parameter integer LOG_N_BANKS = $clog2(N_BANKS),
  35. parameter integer LOG_N_ROWS = $clog2(N_ROWS),
  36. parameter integer LOG_N_COLS = $clog2(N_COLS)
  37. )(
  38. // Write interface - Row store/swap
  39. input wire [LOG_N_BANKS-1:0] wr_bank_addr,
  40. input wire [LOG_N_ROWS-1:0] wr_row_addr,
  41. input wire wr_row_store,
  42. output wire wr_row_rdy,
  43. input wire wr_row_swap,
  44. // Write interface - Access
  45. input wire [BITDEPTH-1:0] wr_data,
  46. input wire [LOG_N_COLS-1:0] wr_col_addr,
  47. input wire wr_en,
  48. // Read interface - Preload
  49. input wire [LOG_N_ROWS-1:0] rd_row_addr,
  50. input wire rd_row_load,
  51. output wire rd_row_rdy,
  52. input wire rd_row_swap,
  53. // Read interface - Access
  54. output wire [(N_BANKS * N_CHANS * N_PLANES)-1:0] rd_data,
  55. input wire [LOG_N_COLS-1:0] rd_col_addr,
  56. input wire rd_en,
  57. // Frame swap request
  58. input wire frame_swap,
  59. // Clock / Reset
  60. input wire clk,
  61. input wire rst
  62. );
  63. // Internal params
  64. // ---------------
  65. // This tries to come up with the best memory layout and sets up
  66. // a bunch of constants appropriately
  67. //
  68. // Address seen from access PoV ( fb_addr signal ) :
  69. // 1 : Buffer Select
  70. // LOG_N_BANKS : Bank selection
  71. // LOG_N_ROWS : Row address
  72. // LOG_N_COLS : Column address
  73. // LOG_FB_DC : Index of the word that compose the full color data
  74. // (when framebuffer width is thinner than BITDEPTH)
  75. // -------------
  76. // FB_AW : (total number of bit in that address)
  77. //
  78. // To map this to the memory address ( mem_addr signal ):
  79. // * Add PAD_BITS at the MSBs (just in case we use less than 1 SPRAM)
  80. // * Drop OMUX_BITS + IMUX_BITS at the LSBs
  81. // - IMUX_BITS used for muxing down the 16 bit wide bus down to
  82. // FB_DW if needed
  83. // - OMUX_BITS used to mux between the SPRAMs used in // to
  84. // increase the total memory depth
  85. `define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b))
  86. `define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
  87. // Round bitdepth to a power of 2 with minimum of 4
  88. localparam integer LOG_BITDEPTH = (BITDEPTH > 4) ? $clog2(BITDEPTH) : 2;
  89. // Number of SPRAM needed for frame buffer
  90. localparam integer LOG_SPRAM_COUNT = `MAX(0, (1 + LOG_N_BANKS + LOG_N_ROWS + LOG_N_COLS + LOG_BITDEPTH) - 18);
  91. localparam integer SPRAM_COUNT = 1 << LOG_SPRAM_COUNT;
  92. // Width of the framebuffer access bus
  93. localparam integer FB_DW = `MIN((16 * SPRAM_COUNT), (1 << LOG_BITDEPTH));
  94. // Number of SPRAM used in 'width-mode'
  95. localparam integer LOG_SPRAM_WIDE = $clog2(`MAX(FB_DW,16)) - 4;
  96. localparam integer SPRAM_WIDE = 1 << LOG_SPRAM_WIDE;
  97. // Number of SPRAM used in 'depth-mode'
  98. localparam integer LOG_SPRAM_DEEP = LOG_SPRAM_COUNT - LOG_SPRAM_WIDE;
  99. localparam integer SPRAM_DEEP = 1 << LOG_SPRAM_DEEP;
  100. // Number of framebuffer words for each pixel
  101. localparam integer LOG_FB_DC = LOG_BITDEPTH - $clog2(FB_DW);
  102. localparam integer FB_DC = 1 << LOG_FB_DC;
  103. // Framebuffer final address width
  104. localparam integer FB_AW = 1 + LOG_N_BANKS + LOG_N_ROWS + LOG_N_COLS + LOG_FB_DC;
  105. // Zero-bits to MSB pad SPRAM address (if using less than 1 SPRAM)
  106. localparam integer PAD_BITS = `MAX(0, 18 - (1 + LOG_N_BANKS + LOG_N_ROWS + LOG_N_COLS + LOG_BITDEPTH));
  107. // Number of bits used for muxing inside the wide memory bus down to FB_DW
  108. localparam integer IMUX_BITS = $clog2(`MAX(1, 16 / FB_DW));
  109. // Number of bits used for muxing between the SPRAM used in // to increase depth
  110. localparam integer OMUX_BITS = LOG_SPRAM_DEEP;
  111. initial begin
  112. $display("Hub75 Frame Buffer config :");
  113. $display(" - SPRAM_COUNT : %d", SPRAM_COUNT);
  114. $display(" - SPRAM_WIDE : %d", SPRAM_WIDE);
  115. $display(" - SPRAM_DEEP : %d", SPRAM_DEEP);
  116. $display(" - FB_AW : %d", FB_AW);
  117. $display(" - FB_DW : %d", FB_DW);
  118. $display(" - FB_DC : %d", FB_DC);
  119. $display(" - PAD_BITS : %d", PAD_BITS);
  120. $display(" - IMUX_BITS : %d", IMUX_BITS);
  121. $display(" - OMUX_BITS : %d", OMUX_BITS);
  122. end
  123. // Signals
  124. // -------
  125. // Arbitration logic
  126. reg arb_busy;
  127. reg arb_prio;
  128. // Write-in control
  129. wire wi_req;
  130. reg wi_gnt;
  131. wire wi_rel;
  132. // Read-out control
  133. wire ro_req;
  134. reg ro_gnt;
  135. wire ro_rel;
  136. // Raw signals from the storage cells
  137. wire [16*SPRAM_WIDE-1:0] mem_di;
  138. wire [16*SPRAM_WIDE-1:0] mem_do [0:SPRAM_DEEP-1];
  139. wire [13:0] mem_addr;
  140. wire [ 3:0] mem_mask;
  141. wire mem_wren [0:SPRAM_DEEP-1];
  142. wire [16*SPRAM_WIDE-1:0] mem_do_mux;
  143. // Frame buffer access
  144. wire [FB_DW-1:0] fb_di;
  145. wire [FB_DW-1:0] fb_do;
  146. wire [FB_AW-1:0] fb_addr;
  147. wire fb_wren;
  148. reg [FB_AW-1:0] fb_addr_r;
  149. reg fb_pingpong;
  150. // Write-in frame buffer access
  151. wire [FB_AW-2:0] wifb_addr;
  152. wire [FB_DW-1:0] wifb_data;
  153. wire wifb_wren;
  154. // Read-out frame-buffer access
  155. wire [FB_AW-2:0] rofb_addr;
  156. wire [FB_DW-1:0] rofb_data;
  157. // Control
  158. // -------
  159. // Arbitration logic
  160. always @(posedge clk or posedge rst)
  161. begin
  162. if (rst) begin
  163. arb_prio <= 1'b0;
  164. arb_busy <= 1'b0;
  165. wi_gnt <= 1'b0;
  166. ro_gnt <= 1'b0;
  167. end else begin
  168. arb_busy <= (arb_busy | wi_req | ro_req) & ~(wi_rel | ro_rel);
  169. arb_prio <= (wi_gnt | ro_gnt) ? ro_gnt : arb_prio;
  170. wi_gnt <= ~arb_busy & wi_req & (~ro_req | arb_prio);
  171. ro_gnt <= ~arb_busy & ro_req & (~wi_req | ~arb_prio);
  172. end
  173. end
  174. // Double-Buffer
  175. always @(posedge clk or posedge rst)
  176. if (rst)
  177. fb_pingpong <= 1'b0;
  178. else
  179. fb_pingpong <= fb_pingpong ^ frame_swap;
  180. // Shared access
  181. // We assume users as well behaved and just use wren for mux control
  182. assign fb_di = wifb_data;
  183. assign rofb_data = fb_do;
  184. assign fb_addr = wifb_wren ? { ~fb_pingpong, wifb_addr } : { fb_pingpong, rofb_addr };
  185. assign fb_wren = wifb_wren;
  186. // Storage
  187. // -------
  188. genvar i, j;
  189. // Generate memory elements
  190. generate
  191. for (i=0; i<SPRAM_DEEP; i=i+1)
  192. begin
  193. for (j=0; j<SPRAM_WIDE; j=j+1)
  194. begin
  195. SB_SPRAM256KA mem_I (
  196. .DATAIN(mem_di[16*j+15:16*j]),
  197. .ADDRESS(mem_addr),
  198. .MASKWREN(mem_mask),
  199. .WREN(mem_wren[i]),
  200. .CHIPSELECT(1'b1),
  201. .CLOCK(clk),
  202. .STANDBY(1'b0),
  203. .SLEEP(1'b0),
  204. .POWEROFF(1'b1),
  205. .DATAOUT(mem_do[i][16*j+15:16*j])
  206. );
  207. end
  208. end
  209. endgenerate
  210. // Register address to have it available for muxing
  211. always @(posedge clk)
  212. fb_addr_r <= fb_addr;
  213. // Map fb_addr -> mem_addr
  214. assign mem_addr = { {(PAD_BITS){1'b0}}, fb_addr[FB_AW-1:OMUX_BITS+IMUX_BITS] };
  215. // Output muxing
  216. generate
  217. // Mux across the SPRAM used in parallel for depth (if needed)
  218. if (OMUX_BITS > 0)
  219. assign mem_do_mux = mem_do[fb_addr_r[OMUX_BITS+IMUX_BITS-1:IMUX_BITS]];
  220. else
  221. assign mem_do_mux = mem_do[0];
  222. // Mux down to FB_DW (if needed)
  223. if (IMUX_BITS > 0)
  224. assign fb_do = mem_do_mux[FB_DW*fb_addr_r[IMUX_BITS-1:0]+:FB_DW];
  225. else
  226. assign fb_do = mem_do_mux;
  227. endgenerate
  228. // Map fb_di -> mem_di
  229. generate
  230. for (i=0; i<(1<<IMUX_BITS); i=i+1)
  231. assign mem_di[FB_DW*i+:FB_DW] = fb_di;
  232. endgenerate
  233. // Input masking / write-enables
  234. generate
  235. // Write Enable
  236. if (OMUX_BITS > 0)
  237. for (i=0; i<SPRAM_DEEP; i=i+1)
  238. assign mem_wren[i] = fb_wren & (fb_addr[IMUX_BITS+:OMUX_BITS] == i);
  239. else
  240. assign mem_wren[0] = fb_wren;
  241. // Mask nibbles (if needed)
  242. if (IMUX_BITS == 2)
  243. assign mem_mask = {
  244. fb_addr[1:0] == 2'b11,
  245. fb_addr[1:0] == 2'b10,
  246. fb_addr[1:0] == 2'b01,
  247. fb_addr[1:0] == 2'b00
  248. };
  249. else if (IMUX_BITS == 1)
  250. assign mem_mask = {
  251. fb_addr[0], fb_addr[0],
  252. ~fb_addr[0], ~fb_addr[0]
  253. };
  254. else
  255. assign mem_mask = 4'hf;
  256. endgenerate
  257. // Write-in
  258. // --------
  259. hub75_fb_writein #(
  260. .N_BANKS(N_BANKS),
  261. .N_ROWS(N_ROWS),
  262. .N_COLS(N_COLS),
  263. .BITDEPTH(BITDEPTH),
  264. .FB_AW(FB_AW-1),
  265. .FB_DW(FB_DW),
  266. .FB_DC(FB_DC)
  267. ) writein_I (
  268. .wr_bank_addr(wr_bank_addr),
  269. .wr_row_addr(wr_row_addr),
  270. .wr_row_store(wr_row_store),
  271. .wr_row_rdy(wr_row_rdy),
  272. .wr_row_swap(wr_row_swap),
  273. .wr_data(wr_data),
  274. .wr_col_addr(wr_col_addr),
  275. .wr_en(wr_en),
  276. .ctrl_req(wi_req),
  277. .ctrl_gnt(wi_gnt),
  278. .ctrl_rel(wi_rel),
  279. .fb_addr(wifb_addr),
  280. .fb_data(wifb_data),
  281. .fb_wren(wifb_wren),
  282. .clk(clk),
  283. .rst(rst)
  284. );
  285. // Read-out
  286. // --------
  287. hub75_fb_readout #(
  288. .N_BANKS(N_BANKS),
  289. .N_ROWS(N_ROWS),
  290. .N_COLS(N_COLS),
  291. .N_CHANS(N_CHANS),
  292. .N_PLANES(N_PLANES),
  293. .BITDEPTH(BITDEPTH),
  294. .FB_AW(FB_AW-1),
  295. .FB_DW(FB_DW),
  296. .FB_DC(FB_DC)
  297. ) readout_I (
  298. .rd_row_addr(rd_row_addr),
  299. .rd_row_load(rd_row_load),
  300. .rd_row_rdy(rd_row_rdy),
  301. .rd_row_swap(rd_row_swap),
  302. .rd_data(rd_data),
  303. .rd_col_addr(rd_col_addr),
  304. .rd_en(rd_en),
  305. .ctrl_req(ro_req),
  306. .ctrl_gnt(ro_gnt),
  307. .ctrl_rel(ro_rel),
  308. .fb_addr(rofb_addr),
  309. .fb_data(rofb_data),
  310. .clk(clk),
  311. .rst(rst)
  312. );
  313. endmodule // hub75_framebuffer