update tsconf to commit 83afbba6f5d366f96297028aa3d64512fa254a51

This commit is contained in:
Eugene Lozovoy
2024-09-12 16:28:38 +03:00
parent ba7d903e12
commit 3681138b01
47 changed files with 5648 additions and 4341 deletions

View File

@ -1,33 +1,35 @@
// This module fetches video data from DRAM
// This module fetches video data from DRAM
`include "tune.v"
module video_fetch
(
// clocks
input wire clk,
// control
input wire [3:0] f_sel,
input wire [1:0] b_sel,
input wire fetch_stb,
// video data
output reg [31:0] fetch_data,
output reg [31:0] fetch_temp,
// DRAM interface
input wire video_strobe,
input wire [15:0] video_data
);
// fetching data
always @(posedge clk) if (video_strobe)
begin
if (f_sel[0]) fetch_temp[ 7: 0] <= b_sel[0] ? video_data[15:8] : video_data[ 7:0];
if (f_sel[1]) fetch_temp[15: 8] <= b_sel[1] ? video_data[15:8] : video_data[ 7:0];
if (f_sel[2]) fetch_temp[23:16] <= video_data[ 7:0];
if (f_sel[3]) fetch_temp[31:24] <= video_data[15:8];
end
always @(posedge clk) if (fetch_stb) fetch_data <= fetch_temp;
(
// clocks
input wire clk,
// control
input wire [3:0] f_sel,
input wire [1:0] b_sel,
input wire fetch_stb,
// video data
output reg [31:0] fetch_data,
output reg [31:0] fetch_temp,
// DRAM interface
input wire video_strobe,
input wire [15:0] video_data
);
always @(posedge clk) if (video_strobe)
begin
if (f_sel[0]) fetch_temp[ 7: 0] <= b_sel[0] ? video_data[15:8] : video_data[ 7:0];
if (f_sel[1]) fetch_temp[15: 8] <= b_sel[1] ? video_data[15:8] : video_data[ 7:0];
if (f_sel[2]) fetch_temp[23:16] <= video_data[ 7:0];
if (f_sel[3]) fetch_temp[31:24] <= video_data[15:8];
end
always @(posedge clk) if (fetch_stb)
fetch_data <= fetch_temp;
endmodule

View File

@ -1,235 +1,230 @@
// This module decodes video modes
// This module decodes video modes
`include "tune.v"
module video_mode
(
// clocks
input wire clk, f1, c3,
// video config
input wire [7:0] vpage,
input wire [7:0] vconf,
input wire ts_rres_ext,
// video parameters & mode controls
input wire [8:0] gx_offs,
output wire [9:0] x_offs_mode,
output wire [8:0] hpix_beg,
output wire [8:0] hpix_end,
output wire [8:0] vpix_beg,
output wire [8:0] vpix_end,
output wire [8:0] hpix_beg_ts,
output wire [8:0] hpix_end_ts,
output wire [8:0] vpix_beg_ts,
output wire [8:0] vpix_end_ts,
output wire [5:0] x_tiles,
output wire [4:0] go_offs,
output wire [3:0] fetch_sel,
output wire [1:0] fetch_bsl,
input wire [3:0] fetch_cnt,
input wire pix_start,
input wire line_start_s,
output wire tv_hires,
output wire [1:0] render_mode,
output wire pix_stb,
output wire fetch_stb,
// video data
input wire [15:0] txt_char,
// video counters
input wire [7:0] cnt_col,
input wire [8:0] cnt_row,
input wire cptr,
// DRAM interface
output wire [20:0] video_addr,
output wire [ 4:0] video_bw
);
wire [1:0] vmod = vconf[1:0];
wire [1:0] rres = vconf[7:6];
// clocking strobe for pixels (TV)
assign pix_stb = tv_hires ? f1 : c3;
// Modes
localparam M_ZX = 2'h0; // ZX
localparam M_HC = 2'h1; // 16c
localparam M_XC = 2'h2; // 256c
localparam M_TX = 2'h3; // Text
// Render modes (affects 'video_render.v')
localparam R_ZX = 2'h0;
localparam R_HC = 2'h1;
localparam R_XC = 2'h2;
localparam R_TX = 2'h3;
// fetch strobes
wire ftch[0:3];
assign fetch_stb = (pix_start | ftch[render_mode]) && c3;
assign ftch[R_ZX] = &fetch_cnt[3:0];
assign ftch[R_HC] = &fetch_cnt[1:0];
assign ftch[R_XC] = fetch_cnt[0];
assign ftch[R_TX] = &fetch_cnt[3:0];
// fetch window
wire [4:0] g_offs[0:3];
// these values are from a thin air!!! recheck them occasionally!
assign g_offs[M_ZX] = 5'd18;
assign g_offs[M_HC] = 5'd6;
assign g_offs[M_XC] = 5'd4;
assign g_offs[M_TX] = 5'd10;
assign go_offs = g_offs[vmod];
// fetch selectors
// Attention: counter is already incremented at the time of video data fetching!
// wire m_c = (vmod == M_HC) | (vmod == M_XC);
// assign fetch_sel = vmod == M_TX ? f_txt_sel[cnt_col[1:0]] : {~cptr, ~cptr, cptr | m_c, cptr | m_c};
// wire [3:0] f_sel[0:7];
wire [3:0] f_sel[0:3];
assign f_sel[M_ZX] = {~cptr, ~cptr, cptr, cptr};
assign f_sel[M_HC] = {~cptr, ~cptr, 2'b11};
assign f_sel[M_XC] = {~cptr, ~cptr, 2'b11};
assign f_sel[M_TX] = f_txt_sel[cnt_col[1:0]];
assign fetch_sel = f_sel[vmod];
assign fetch_bsl = (vmod == M_TX) ? f_txt_bsl[cnt_col[1:0]] : 2'b10;
// wire [1:0] f_bsl[0:7];
// assign f_bsl[M_ZX] = 2'b10;
// assign f_bsl[M_HC] = 2'b10;
// assign f_bsl[M_XC] = 2'b10;
// assign f_bsl[M_TX] = f_txt_bsl[cnt_col[1:0]];
// assign fetch_bsl = f_bsl[vmod];
wire [3:0] f_txt_sel[0:3];
assign f_txt_sel[1] = 4'b0011; // char
assign f_txt_sel[2] = 4'b1100; // attr
assign f_txt_sel[3] = 4'b0001; // gfx0
assign f_txt_sel[0] = 4'b0010; // gfx1
wire [1:0] f_txt_bsl[0:3];
assign f_txt_bsl[1] = 2'b10; // char
assign f_txt_bsl[2] = 2'b10; // attr
assign f_txt_bsl[3] = {2{cnt_row[0]}}; // gfx0
assign f_txt_bsl[0] = {2{cnt_row[0]}}; // gfx1
// X offset
assign x_offs_mode = {vmod == M_XC ? {gx_offs[8:1], 1'b0} : {1'b0, gx_offs[8:1]}, gx_offs[0]};
// DRAM bandwidth usage
localparam BW2 = 2'b00;
localparam BW4 = 2'b01;
localparam BW8 = 2'b11;
localparam BU1 = 3'b001;
localparam BU2 = 3'b010;
localparam BU4 = 3'b100;
// [4:3] - total cycles: 11 = 8 / 01 = 4 / 00 = 2
// [2:0] - need cycles
wire [4:0] bw[0:3];
assign bw[M_ZX] = {BW8, BU1}; // '1 of 8' (ZX)
assign bw[M_HC] = {BW4, BU1}; // '1 of 4' (16c)
assign bw[M_XC] = {BW2, BU1}; // '1 of 2' (256c)
assign bw[M_TX] = {BW8, BU4}; // '4 of 8' (text)
assign video_bw = bw[vmod];
// pixelrate
wire [3:0] pixrate = 4'b1000; // change these if you change the modes indexes!
assign tv_hires = pixrate[vmod];
// render mode
// wire [1:0] r_mode[0:7];
wire [1:0] r_mode[0:3];
assign r_mode[M_ZX] = R_ZX;
assign r_mode[M_HC] = R_HC;
assign r_mode[M_XC] = R_XC;
assign r_mode[M_TX] = R_TX;
assign render_mode = r_mode[vmod];
// raster resolution
wire [8:0] hp_beg[0:3];
wire [8:0] hp_end[0:3];
wire [8:0] vp_beg[0:3];
wire [8:0] vp_end[0:3];
wire [5:0] x_tile[0:3];
assign hp_beg[0] = 9'd136; // 256 (88-52-256-52)
assign hp_beg[1] = 9'd108; // 320 (88-20-320-20)
assign hp_beg[2] = 9'd108; // 320 (88-20-320-20)
assign hp_beg[3] = 9'd88; // 360 (88-0-360-0)
assign hp_end[0] = 9'd392; // 256
assign hp_end[1] = 9'd428; // 320
assign hp_end[2] = 9'd428; // 320
assign hp_end[3] = 9'd448; // 360
assign vp_beg[0] = 9'd080; // 192 (22-24-192-24)/(32-48-192-48) (blank-border-pixels-border)
assign vp_beg[1] = 9'd076; // 200 (22-20-200-20)/(32-44-200-44)
assign vp_beg[2] = 9'd056; // 240 (22-0-240-0)/(32-24-240-24)
assign vp_beg[3] = 9'd032; // 288 (22-0-240-0)/(32-0-288-0)
assign vp_end[0] = 9'd272; // 192
assign vp_end[1] = 9'd276; // 200
assign vp_end[2] = 9'd296; // 240
assign vp_end[3] = 9'd320; // 240/288
assign x_tile[0] = 6'd34; // 256
assign x_tile[1] = 6'd42; // 320
assign x_tile[2] = 6'd42; // 320
assign x_tile[3] = 6'd47; // 360
assign hpix_beg = hp_beg[rres];
assign hpix_end = hp_end[rres];
assign vpix_beg = vp_beg[rres];
assign vpix_end = vp_end[rres];
(
// clocks
input wire clk, f1, c3,
assign hpix_beg_ts = ts_rres_ext ? hp_beg[3] : hp_beg[rres];
assign hpix_end_ts = ts_rres_ext ? hp_end[3] : hp_end[rres];
assign vpix_beg_ts = ts_rres_ext ? vp_beg[3] : vp_beg[rres];
assign vpix_end_ts = ts_rres_ext ? vp_end[3] : vp_end[rres];
// video config
input wire [7:0] vpage,
input wire [7:0] vconf,
input wire ts_rres_ext,
input wire v60hz,
assign x_tiles = ts_rres_ext ? x_tile[3] : x_tile[rres];
// videomode addresses
wire [20:0] v_addr[0:3];
assign v_addr[M_ZX] = addr_zx;
assign v_addr[M_HC] = addr_16c;
assign v_addr[M_XC] = addr_256c;
assign v_addr[M_TX] = addr_text;
assign video_addr = v_addr[vmod];
// ZX
wire [20:0] addr_zx = {vpage, 1'b0, ~cnt_col[0] ? addr_zx_gfx : addr_zx_atr};
wire [11:0] addr_zx_gfx = {cnt_row[7:6], cnt_row[2:0], cnt_row[5:3], cnt_col[4:1]};
wire [11:0] addr_zx_atr = {3'b110, cnt_row[7:3], cnt_col[4:1]};
// 16c
wire [20:0] addr_16c = {vpage[7:3], cnt_row, cnt_col[6:0]};
// 256c
wire [20:0] addr_256c = {vpage[7:4], cnt_row, cnt_col[7:0]};
// Textmode
wire [20:0] addr_text = {vpage[7:1], addr_tx[cnt_col[1:0]]};
wire [13:0] addr_tx[0:3];
assign addr_tx[0] = {vpage[0], cnt_row[8:3], 1'b0, cnt_col[7:2]}; // char codes, data[15:0]
assign addr_tx[1] = {vpage[0], cnt_row[8:3], 1'b1, cnt_col[7:2]}; // char attributes, data[31:16]
assign addr_tx[2] = {~vpage[0], 3'b000, (txt_char[7:0]), cnt_row[2:1]}; // char0 graphics, data[7:0]
assign addr_tx[3] = {~vpage[0], 3'b000, (txt_char[15:8]), cnt_row[2:1]}; // char1 graphics, data[15:8]
endmodule
// video parameters & mode controls
input wire [8:0] gx_offs,
output wire [9:0] x_offs_mode,
output wire [8:0] hpix_beg,
output wire [8:0] hpix_end,
output wire [8:0] vpix_beg,
output wire [8:0] vpix_end,
output wire [8:0] hpix_beg_ts,
output wire [8:0] hpix_end_ts,
output wire [8:0] vpix_beg_ts,
output wire [8:0] vpix_end_ts,
output wire [5:0] x_tiles,
output wire [4:0] go_offs,
output wire [3:0] fetch_sel,
output wire [1:0] fetch_bsl,
input wire [3:0] fetch_cnt,
input wire pix_start,
input wire line_start_s,
output wire tv_hires,
output reg vga_hires = 0,
output wire [1:0] render_mode,
output wire pix_stb,
output wire fetch_stb,
// video data
input wire [15:0] txt_char,
// video counters
input wire [7:0] cnt_col,
input wire [8:0] cnt_row,
input wire cptr,
// DRAM interface
output wire [20:0] video_addr,
output wire [ 4:0] video_bw
);
wire [1:0] vmod = vconf[1:0];
wire [1:0] rres = vconf[7:6];
// clocking strobe for pixels (TV)
assign pix_stb = tv_hires ? f1 : c3;
always @(posedge clk)
if (line_start_s)
vga_hires <= tv_hires;
// Modes
localparam M_ZX = 2'h0; // ZX
localparam M_HC = 2'h1; // 16c
localparam M_XC = 2'h2; // 256c
localparam M_TX = 2'h3; // Text
// Render modes (affects 'video_render.v')
localparam R_ZX = 2'h0;
localparam R_HC = 2'h1;
localparam R_XC = 2'h2;
localparam R_TX = 2'h3;
// fetch strobes
wire ftch[0:3];
assign fetch_stb = (pix_start | ftch[render_mode]) && c3;
assign ftch[R_ZX] = &fetch_cnt[3:0];
assign ftch[R_HC] = &fetch_cnt[1:0];
assign ftch[R_XC] = fetch_cnt[0];
assign ftch[R_TX] = &fetch_cnt[3:0];
// fetch window
wire [4:0] g_offs[0:3];
assign g_offs[M_ZX] = 5'd18;
assign g_offs[M_HC] = 5'd6;
assign g_offs[M_XC] = 5'd4;
assign g_offs[M_TX] = 5'd10;
assign go_offs = g_offs[vmod];
// fetch selectors
// Attention: counter is already incremented at the time of video data fetching!
wire [3:0] f_sel[0:3];
wire [3:0] f_txt_sel[0:3];
wire [1:0] f_txt_bsl[0:3];
assign f_sel[M_ZX] = {~cptr, ~cptr, cptr, cptr};
assign f_sel[M_HC] = {~cptr, ~cptr, 2'b11};
assign f_sel[M_XC] = {~cptr, ~cptr, 2'b11};
assign f_sel[M_TX] = f_txt_sel[cnt_col[1:0]];
assign fetch_sel = f_sel[vmod];
assign fetch_bsl = (vmod == M_TX) ? f_txt_bsl[cnt_col[1:0]] : 2'b10;
assign f_txt_sel[1] = 4'b0011; // char
assign f_txt_sel[2] = 4'b1100; // attr
assign f_txt_sel[3] = 4'b0001; // gfx0
assign f_txt_sel[0] = 4'b0010; // gfx1
assign f_txt_bsl[1] = 2'b10; // char
assign f_txt_bsl[2] = 2'b10; // attr
assign f_txt_bsl[3] = {2{cnt_row[0]}}; // gfx0
assign f_txt_bsl[0] = {2{cnt_row[0]}}; // gfx1
// X offset
assign x_offs_mode = {vmod == M_XC ? {gx_offs[8:1], 1'b0} : {1'b0, gx_offs[8:1]}, gx_offs[0]};
// DRAM bandwidth usage
localparam BW2 = 2'b00;
localparam BW4 = 2'b01;
localparam BW8 = 2'b11;
localparam BU1 = 3'b001;
localparam BU2 = 3'b010;
localparam BU4 = 3'b100;
// [4:3] - total cycles: 11 = 8 / 01 = 4 / 00 = 2
// [2:0] - need cycles
wire [4:0] bw[0:3];
assign bw[M_ZX] = {BW8, BU1}; // '1 of 8' (ZX)
assign bw[M_HC] = {BW4, BU1}; // '1 of 4' (16c)
assign bw[M_XC] = {BW2, BU1}; // '1 of 2' (256c)
assign bw[M_TX] = {BW8, BU4}; // '4 of 8' (text)
assign video_bw = bw[vmod];
// pixelrate
wire [3:0] pixrate = 4'b1000; // change these if you change the modes indexes!
assign tv_hires = pixrate[vmod];
// render mode
wire [1:0] r_mode[0:3];
assign r_mode[M_ZX] = R_ZX;
assign r_mode[M_HC] = R_HC;
assign r_mode[M_XC] = R_XC;
assign r_mode[M_TX] = R_TX;
assign render_mode = r_mode[vmod];
// raster resolution
wire [8:0] hp_beg[0:3];
wire [8:0] hp_end[0:3];
wire [8:0] vp_beg[0:3];
wire [8:0] vp_end[0:3];
wire [5:0] x_tile[0:3];
assign hp_beg[0] = 9'd140; // 256 (88-52-256-52)
assign hp_beg[1] = 9'd108; // 320 (88-20-320-20)
assign hp_beg[2] = 9'd108; // 320 (88-20-320-20)
assign hp_beg[3] = 9'd88; // 360 (88-0-360-0)
assign hp_end[0] = 9'd396; // 256
assign hp_end[1] = 9'd428; // 320
assign hp_end[2] = 9'd428; // 320
assign hp_end[3] = 9'd448; // 360
`ifdef PENT_312
assign vp_beg[0] = v60hz ? 9'd046 : 9'd072; // 192 (22-24-192-24)/(24-48-192-48) (blank-border-pixels-border)
assign vp_beg[1] = v60hz ? 9'd042 : 9'd068; // 200 (22-20-200-20)/(24-44-200-44)
assign vp_beg[2] = v60hz ? 9'd022 : 9'd048; // 240 (22-0-240-0)/(24-24-240-24)
assign vp_beg[3] = v60hz ? 9'd022 : 9'd024; // 240/288 (22-0-240-0)/(24-0-288-0)
assign vp_end[0] = v60hz ? 9'd238 : 9'd264; // 192
assign vp_end[1] = v60hz ? 9'd242 : 9'd268; // 200
assign vp_end[2] = v60hz ? 9'd262 : 9'd288; // 240
assign vp_end[3] = v60hz ? 9'd262 : 9'd312; // 240/288
`else
assign vp_beg[0] = v60hz ? 9'd046 : 9'd080; // 192 (22-24-192-24)/(32-48-192-48) (blank-border-pixels-border)
assign vp_beg[1] = v60hz ? 9'd042 : 9'd076; // 200 (22-20-200-20)/(32-44-200-44)
assign vp_beg[2] = v60hz ? 9'd022 : 9'd056; // 240 (22-0-240-0)/(32-24-240-24)
assign vp_beg[3] = v60hz ? 9'd022 : 9'd032; // 240/288 (22-0-240-0)/(32-0-288-0)
assign vp_end[0] = v60hz ? 9'd238 : 9'd272; // 192
assign vp_end[1] = v60hz ? 9'd242 : 9'd276; // 200
assign vp_end[2] = v60hz ? 9'd262 : 9'd296; // 240
assign vp_end[3] = v60hz ? 9'd262 : 9'd320; // 240/288
`endif
assign x_tile[0] = 6'd34; // 256
assign x_tile[1] = 6'd42; // 320
assign x_tile[2] = 6'd42; // 320
assign x_tile[3] = 6'd47; // 360
assign hpix_beg = hp_beg[rres];
assign hpix_end = hp_end[rres];
assign vpix_beg = vp_beg[rres];
assign vpix_end = vp_end[rres];
assign hpix_beg_ts = ts_rres_ext ? hp_beg[3] : hp_beg[rres];
assign hpix_end_ts = ts_rres_ext ? hp_end[3] : hp_end[rres];
assign vpix_beg_ts = ts_rres_ext ? vp_beg[3] : vp_beg[rres];
assign vpix_end_ts = ts_rres_ext ? vp_end[3] : vp_end[rres];
assign x_tiles = ts_rres_ext ? x_tile[3] : x_tile[rres];
// ZX
wire [11:0] addr_zx_gfx = {cnt_row[7:6], cnt_row[2:0], cnt_row[5:3], cnt_col[4:1]};
wire [11:0] addr_zx_atr = {3'b110, cnt_row[7:3], cnt_col[4:1]};
wire [20:0] addr_zx = {vpage, 1'b0, ~cnt_col[0] ? addr_zx_gfx : addr_zx_atr};
// 16c
wire [20:0] addr_16c = {vpage[7:3], cnt_row, cnt_col[6:0]};
// 256c
wire [20:0] addr_256c = {vpage[7:4], cnt_row, cnt_col[7:0]};
// Textmode
wire [13:0] addr_tx[0:3];
wire [20:0] addr_text = {vpage[7:1], addr_tx[cnt_col[1:0]]};
assign addr_tx[0] = {vpage[0], cnt_row[8:3], 1'b0, cnt_col[7:2]}; // char codes, data[15:0]
assign addr_tx[1] = {vpage[0], cnt_row[8:3], 1'b1, cnt_col[7:2]}; // char attributes, data[31:16]
assign addr_tx[2] = {~vpage[0], 3'b000, (txt_char[7:0]), cnt_row[2:1]}; // char0 graphics, data[7:0]
assign addr_tx[3] = {~vpage[0], 3'b000, (txt_char[15:8]), cnt_row[2:1]}; // char1 graphics, data[15:8]
// videomode addresses
wire [20:0] v_addr[0:3];
assign v_addr[M_ZX] = addr_zx;
assign v_addr[M_HC] = addr_16c;
assign v_addr[M_XC] = addr_256c;
assign v_addr[M_TX] = addr_text;
assign video_addr = v_addr[vmod];
endmodule

View File

@ -2,59 +2,181 @@
// This module generates video for DAC
// (c)2015 TSL
`include "tune.v"
module video_out
(
// clocks
input wire clk, c3,
// clocks
input wire clk, c3,
// video controls
input wire tv_blank,
input wire [1:0] plex_sel_in,
// video controls
input wire vga_on,
input wire tv_blank,
input wire vga_blank,
input wire vga_line,
input wire [1:0] plex_sel_in,
// mode controls
input wire tv_hires,
input wire [3:0] palsel,
// mode controls
input wire tv_hires,
input wire vga_hires,
input wire [3:0] palsel,
// Z80 pins
input wire [15:0] cram_data_in,
input wire [7:0] cram_addr_in,
input wire cram_we,
// Z80 pins
input wire [15:0] cram_data_in,
input wire [7:0] cram_addr_in,
input wire cram_we,
// video data
input wire [7:0] vplex_in,
output wire [7:0] vred,
output wire [7:0] vgrn,
output wire [7:0] vblu,
output wire vdac_mode
// video data
input wire [7:0] vplex_in,
input wire [7:0] vgaplex,
output wire [1:0] vred,
output wire [1:0] vgrn,
output wire [1:0] vblu,
output wire [4:0] vred_raw,
output wire [4:0] vgrn_raw,
output wire [4:0] vblu_raw,
output wire vdac_mode
);
wire [14:0] vpix;
wire [15:0] vpixel;
wire [1:0] phase;
wire [7:0] pwm[0:7];
reg [7:0] vplex;
always @(posedge clk) if (c3) vplex <= vplex_in;
reg blank1; // GOVNOKOD!!!!!!!!!!!!!!!!!!!!!
wire [7:0] vdata = tv_hires ? {palsel, plex_sel_in[1] ? vplex[3:0] : vplex[7:4]} : vplex;
assign vred_raw = vpix[14:10];
assign vgrn_raw = vpix[9:5];
assign vblu_raw = vpix[4:0];
assign vdac_mode = vpixel[15];
// TV/VGA mux
reg [7:0] vplex;
always @(posedge clk) if (c3)
vplex <= vplex_in;
wire [7:0] plex = vga_on ? vgaplex : vplex;
wire hires = vga_on ? vga_hires : tv_hires;
wire plex_sel = vga_on ? plex_sel_in[0] : plex_sel_in[1];
wire [7:0] vdata = hires ? {palsel, plex_sel ? plex[3:0] : plex[7:4]} : plex;
wire blank = vga_on ? vga_blank : tv_blank;
assign vpix = blank1 ? 15'b0 : vpixel[14:0];
// assign vpix = blank1 ? 15'b0 : (vpixel[14:0] & 15'b111001110011100); // test for 373 colors
// assign vpix = blank1 ? 15'b0 : (vpixel[14:0] & 15'b110001100011000); // test for 64 colors
// GOVNOKOD!!!!!!!!!!!!!!!!!!!!!
always @(posedge clk)
begin
blank1 <= blank;
end
// color components extraction
wire [1:0] cred = vpix[14:13];
wire [2:0] ired = vpix[12:10];
wire [1:0] cgrn = vpix[ 9: 8];
wire [2:0] igrn = vpix[ 7: 5];
wire [1:0] cblu = vpix[ 4: 3];
wire [2:0] iblu = vpix[ 2: 0];
// prepare and clocking two phases of output
reg [1:0] red0;
reg [1:0] grn0;
reg [1:0] blu0;
reg [1:0] red1;
reg [1:0] grn1;
reg [1:0] blu1;
always @(posedge clk)
begin
red0 <= (!pwm[ired][{phase, 1'b0}] | &cred) ? cred : (cred + 2'b1);
grn0 <= (!pwm[igrn][{phase, 1'b0}] | &cgrn) ? cgrn : (cgrn + 2'b1);
blu0 <= (!pwm[iblu][{phase, 1'b0}] | &cblu) ? cblu : (cblu + 2'b1);
red1 <= (!pwm[ired][{phase, 1'b1}] | &cred) ? cred : (cred + 2'b1);
grn1 <= (!pwm[igrn][{phase, 1'b1}] | &cgrn) ? cgrn : (cgrn + 2'b1);
blu1 <= (!pwm[iblu][{phase, 1'b1}] | &cblu) ? cblu : (cblu + 2'b1);
end
`ifdef IDE_VDAC
// no PWM
assign vred = cred;
assign vgrn = cgrn;
assign vblu = cblu;
`elsif IDE_VDAC2
// no PWM
assign vred = cred;
assign vgrn = cgrn;
assign vblu = cblu;
`else
// output muxing for 56MHz PWM resolution
assign vred = clk ? red1 : red0;
assign vgrn = clk ? grn1 : grn0;
assign vblu = clk ? blu1 : blu0;
`endif
// PWM phase
reg [1:0] ph;
always @(posedge clk)
ph <= ph + 2'b1;
assign phase = {vga_on ? vga_line : ph[1], ph[0]};
// PWM
assign pwm[0] = 8'b00000000;
assign pwm[1] = 8'b00000001;
assign pwm[2] = 8'b01000001;
assign pwm[3] = 8'b01000101;
assign pwm[4] = 8'b10100101;
assign pwm[5] = 8'b10100111;
assign pwm[6] = 8'b11010111;
assign pwm[7] = 8'b11011111;
// CRAM
wire [15:0] vpixel;
dpram #(.DATAWIDTH(16), .ADDRWIDTH(8), .MEM_INIT_FILE("rtl/video/video_cram.mif")) video_cram
(
.clock (clk),
.address_a(cram_addr_in),
.data_a (cram_data_in),
.wren_a (cram_we),
.address_b(vdata),
.q_b (vpixel)
);
reg blank;
always @(posedge clk) blank <= tv_blank;
wire [14:0] vpix = blank ? 15'b0 : vpixel[14:0];
assign vred = {vpix[14:10], vpix[14:12]};
assign vgrn = {vpix[ 9: 5], vpix[ 9: 7]};
assign vblu = {vpix[ 4: 0], vpix[ 4: 2]};
assign vdac_mode = vpixel[15];
dpram #(.DATAWIDTH(16), .ADDRWIDTH(8), .MEM_INIT_FILE("rtl/video/video_cram.mif")) video_cram
(
.clock (clk),
.address_a(cram_addr_in),
.data_a (cram_data_in),
.wren_a (cram_we),
.address_b(vdata),
.q_b (vpixel)
);
/*
altdpram video_cram
(
.inclock (clk),
.data (cram_data_in),
.rdaddress (vdata),
.wraddress (cram_addr_in),
.wren (cram_we),
.q (vpixel),
.aclr (1'b0),
.byteena (1'b1),
.inclocken (1'b1),
.outclock (1'b1),
.outclocken (1'b1),
.rdaddressstall (1'b0),
.rden (1'b1),
.wraddressstall (1'b0)
);
defparam
video_cram.indata_aclr = "OFF",
video_cram.indata_reg = "INCLOCK",
video_cram.intended_device_family = "ACEX1K",
video_cram.lpm_file = "../video/mem/video_cram.mif",
video_cram.lpm_type = "altdpram",
video_cram.outdata_aclr = "OFF",
video_cram.outdata_reg = "UNREGISTERED",
video_cram.rdaddress_aclr = "OFF",
video_cram.rdaddress_reg = "INCLOCK",
video_cram.rdcontrol_aclr = "OFF",
video_cram.rdcontrol_reg = "UNREGISTERED",
video_cram.width = 16,
video_cram.widthad = 8,
video_cram.wraddress_aclr = "OFF",
video_cram.wraddress_reg = "INCLOCK",
video_cram.wrcontrol_aclr = "OFF",
video_cram.wrcontrol_reg = "INCLOCK";
*/
endmodule

View File

@ -1,149 +1,166 @@
// This module latches all port parameters for video from Z80
module video_ports
(
// clocks
input wire clk,
input wire [ 7:0] d,
input wire res,
input wire int_start,
input wire line_start_s,
// port write strobes
input wire zborder_wr,
input wire border_wr,
input wire zvpage_wr,
input wire vpage_wr,
input wire vconf_wr,
input wire gx_offsl_wr,
input wire gx_offsh_wr,
input wire gy_offsl_wr,
input wire gy_offsh_wr,
input wire t0x_offsl_wr,
input wire t0x_offsh_wr,
input wire t0y_offsl_wr,
input wire t0y_offsh_wr,
input wire t1x_offsl_wr,
input wire t1x_offsh_wr,
input wire t1y_offsl_wr,
input wire t1y_offsh_wr,
input wire tsconf_wr,
input wire palsel_wr,
input wire tmpage_wr,
input wire t0gpage_wr,
input wire t1gpage_wr,
input wire sgpage_wr,
input wire hint_beg_wr ,
input wire vint_begl_wr,
input wire vint_begh_wr,
// video parameters
output reg [7:0] border,
output reg [7:0] vpage,
output reg [7:0] vconf,
output reg [8:0] gx_offs,
output reg [8:0] gy_offs,
output reg [8:0] t0x_offs,
output reg [8:0] t0y_offs,
output reg [8:0] t1x_offs,
output reg [8:0] t1y_offs,
output reg [7:0] palsel,
output reg [7:0] hint_beg,
output reg [8:0] vint_beg,
output reg [7:0] tsconf,
output reg [7:0] tmpage,
output reg [7:0] t0gpage,
output reg [7:0] t1gpage,
output reg [7:0] sgpage
);
reg [7:0] vpage_r;
reg [7:0] vconf_r;
reg [7:0] t0gpage_r;
reg [7:0] t1gpage_r;
reg [8:0] gx_offs_r;
reg [8:0] t0x_offs_r;
reg [8:0] t1x_offs_r;
reg [7:0] palsel_r;
wire [8:0] vint_beg_inc = vint_beg + vint_inc;
wire [8:0] vint_beg_next = {(vint_beg_inc[8:6] == 3'b101) ? 3'b0 : vint_beg_inc[8:6], vint_beg_inc[5:0]}; // if over 319 lines, decrement 320
// This module latches all port parameters for video from Z80
reg [3:0] vint_inc;
always @(posedge clk) begin
if (res) begin
vint_beg <= 9'd0;
vint_inc <= 4'b0;
end
else if (vint_begl_wr) vint_beg[7:0] <= d;
else if (vint_begh_wr) begin
vint_beg[8] <= d[0];
vint_inc <= d[7:4];
end
else if (int_start) vint_beg <= vint_beg_next;
end
always @(posedge clk) begin
if (res) begin
vpage_r <= 8'h05;
vconf_r <= 8'h00;
gx_offs_r <= 9'b0;
palsel_r <= 8'h0F;
gy_offs <= 9'b0;
tsconf <= 8'b0;
hint_beg <= 8'd1;
end
else begin
if (zborder_wr ) border <= {palsel[3:0], 1'b0, d[2:0]};
if (border_wr ) border <= d;
if (gy_offsl_wr ) gy_offs[7:0] <= d;
if (gy_offsh_wr ) gy_offs[8] <= d[0];
if (t0y_offsl_wr) t0y_offs[7:0] <= d;
if (t0y_offsh_wr) t0y_offs[8] <= d[0];
if (t1y_offsl_wr) t1y_offs[7:0] <= d;
if (t1y_offsh_wr) t1y_offs[8] <= d[0];
if (tsconf_wr ) tsconf <= d;
if (tmpage_wr ) tmpage <= d;
if (sgpage_wr ) sgpage <= d;
if (hint_beg_wr ) hint_beg <= d;
if (zvpage_wr ) vpage_r <= {6'b000001, d[3], 1'b1};
if (vpage_wr ) vpage_r <= d;
if (vconf_wr ) vconf_r <= d;
if (gx_offsl_wr ) gx_offs_r[7:0] <= d;
if (gx_offsh_wr ) gx_offs_r[8] <= d[0];
if (palsel_wr ) palsel_r <= d;
if (t0x_offsl_wr) t0x_offs_r[7:0] <= d;
if (t0x_offsh_wr) t0x_offs_r[8] <= d[0];
if (t1x_offsl_wr) t1x_offs_r[7:0] <= d;
if (t1x_offsh_wr) t1x_offs_r[8] <= d[0];
if (t0gpage_wr ) t0gpage_r <= d;
if (t1gpage_wr ) t1gpage_r <= d;
end
end
// latching regs at line start, delaying hires for 1 line
always @(posedge clk) begin
if (res)
begin
vpage <= 8'h05;
vconf <= 8'h00;
gx_offs <= 9'b0;
palsel <= 8'h0F;
end
else if (zvpage_wr) vpage <= {6'b000001, d[3], 1'b1};
else if (line_start_s) begin
vpage <= vpage_r;
vconf <= vconf_r;
gx_offs <= gx_offs_r;
palsel <= palsel_r;
t0x_offs <= t0x_offs_r;
t1x_offs <= t1x_offs_r;
t0gpage <= t0gpage_r;
t1gpage <= t1gpage_r;
end
end
endmodule
`include "tune.v"
module video_ports
(
// clocks
input wire clk,
input wire [ 7:0] d,
input wire res,
input wire int_start,
input wire line_start_s,
// port write strobes
input wire zborder_wr,
input wire border_wr,
input wire zvpage_wr,
input wire vpage_wr,
input wire vconf_wr,
input wire gx_offsl_wr,
input wire gx_offsh_wr,
input wire gy_offsl_wr,
input wire gy_offsh_wr,
input wire t0x_offsl_wr,
input wire t0x_offsh_wr,
input wire t0y_offsl_wr,
input wire t0y_offsh_wr,
input wire t1x_offsl_wr,
input wire t1x_offsh_wr,
input wire t1y_offsl_wr,
input wire t1y_offsh_wr,
input wire tsconf_wr,
input wire palsel_wr,
input wire tmpage_wr,
input wire t0gpage_wr,
input wire t1gpage_wr,
input wire sgpage_wr,
input wire hint_beg_wr ,
input wire vint_begl_wr,
input wire vint_begh_wr,
// video parameters
output reg [7:0] border = 0,
output reg [7:0] vpage = 0,
output reg [7:0] vconf = 0,
output reg [8:0] gx_offs = 0,
output reg [8:0] gy_offs = 0,
output reg [8:0] t0x_offs = 0,
output reg [8:0] t0y_offs = 0,
output reg [8:0] t1x_offs = 0,
output reg [8:0] t1y_offs = 0,
output reg [7:0] palsel = 0,
output reg [7:0] hint_beg = 0,
output reg [8:0] vint_beg = 0,
output reg [7:0] tsconf = 0,
output reg [7:0] tmpage = 0,
output reg [7:0] t0gpage = 0,
output reg [7:0] t1gpage = 0,
output reg [7:0] sgpage = 0
);
reg [7:0] vpage_r = 0;
reg [7:0] vconf_r = 0;
reg [7:0] t0gpage_r = 0;
reg [7:0] t1gpage_r = 0;
reg [8:0] gx_offs_r = 0;
reg [8:0] t0x_offs_r = 0;
reg [8:0] t1x_offs_r = 0;
reg [7:0] palsel_r = 0;
reg [3:0] vint_inc = 0;
wire [8:0] vint_beg_inc = vint_beg + vint_inc;
wire [8:0] vint_beg_next = {(vint_beg_inc[8:6] == 3'b101) ? 3'b0 : vint_beg_inc[8:6], vint_beg_inc[5:0]}; // if over 319 lines, decrement 320
always @(posedge clk or posedge res)
if (res)
begin
vint_beg <= 9'd0;
vint_inc <= 4'b0;
end
else if (vint_begl_wr)
vint_beg[7:0] <= d;
else if (vint_begh_wr)
begin
vint_beg[8] <= d[0];
vint_inc <= d[7:4];
end
else if (int_start)
vint_beg <= vint_beg_next;
always @(posedge clk or posedge res)
if (res)
begin
vpage_r <= 8'h05;
vconf_r <= 8'h00;
gx_offs_r <= 9'b0;
palsel_r <= 8'h0F;
gy_offs <= 9'b0;
tsconf <= 8'b0;
hint_beg <= 8'd1;
end
else
begin
if (zborder_wr ) border <= {palsel[3:0], 1'b0, d[2:0]};
if (border_wr ) border <= d;
if (gy_offsl_wr ) gy_offs[7:0] <= d;
if (gy_offsh_wr ) gy_offs[8] <= d[0];
if (t0y_offsl_wr) t0y_offs[7:0] <= d;
if (t0y_offsh_wr) t0y_offs[8] <= d[0];
if (t1y_offsl_wr) t1y_offs[7:0] <= d;
if (t1y_offsh_wr) t1y_offs[8] <= d[0];
if (tsconf_wr ) tsconf <= d;
if (tmpage_wr ) tmpage <= d;
if (sgpage_wr ) sgpage <= d;
if (hint_beg_wr ) hint_beg <= d;
if (zvpage_wr ) vpage_r <= {6'b000001, d[3], 1'b1};
if (vpage_wr ) vpage_r <= d;
if (vconf_wr ) vconf_r <= d;
if (gx_offsl_wr ) gx_offs_r[7:0] <= d;
if (gx_offsh_wr ) gx_offs_r[8] <= d[0];
if (palsel_wr ) palsel_r <= d;
if (t0x_offsl_wr) t0x_offs_r[7:0] <= d;
if (t0x_offsh_wr) t0x_offs_r[8] <= d[0];
if (t1x_offsl_wr) t1x_offs_r[7:0] <= d;
if (t1x_offsh_wr) t1x_offs_r[8] <= d[0];
if (t0gpage_wr ) t0gpage_r <= d;
if (t1gpage_wr ) t1gpage_r <= d;
end
// latching regs at line start, delaying hires for 1 line
always @(posedge clk or posedge res)
if (res)
begin
vpage <= 8'h05;
`ifdef FORCE_TEXT_MODE
vconf <= 8'h83;
`else
vconf <= 8'h00;
`endif
gx_offs <= 9'b0;
palsel <= 8'h0F;
end
else if (zvpage_wr)
vpage <= {6'b000001, d[3], 1'b1};
else if (line_start_s)
begin
vpage <= vpage_r;
`ifndef FORCE_TEXT_MODE
vconf <= vconf_r;
`endif
gx_offs <= gx_offs_r;
palsel <= palsel_r;
t0x_offs <= t0x_offs_r;
t1x_offs <= t1x_offs_r;
t0gpage <= t0gpage_r;
t1gpage <= t1gpage_r;
end
endmodule

View File

@ -1,84 +1,87 @@
// This module renders video data for output
// This module renders video data for output
`include "tune.v"
module video_render
(
// clocks
input wire clk, c1,
// video controls
input wire hvpix,
input wire hvtspix,
input wire nogfx,
input wire notsu,
input wire gfxovr,
input wire flash,
input wire hires,
input wire [3:0] psel,
input wire [3:0] palsel,
// mode controls
input wire [1:0] render_mode,
// video data
input wire [31:0] data,
input wire [ 7:0] border_in,
input wire [ 7:0] tsdata_in,
output wire [ 7:0] vplex_out
);
localparam R_ZX = 2'h0;
localparam R_HC = 2'h1;
localparam R_XC = 2'h2;
localparam R_TX = 2'h3;
// ZX graphics
wire [15:0] zx_gfx = data[15: 0];
wire [15:0] zx_atr = data[31:16];
wire zx_dot = zx_gfx[{psel[3], ~psel[2:0]}];
wire [7:0] zx_attr = ~psel[3] ? zx_atr[7:0] : zx_atr[15:8];
wire [7:0] zx_pix = {palsel, zx_attr[6], zx_dot ^ (flash & zx_attr[7]) ? zx_attr[2:0] : zx_attr[5:3]};
// text graphics
// (it uses common renderer with ZX, but different attributes)
wire [7:0] tx_pix = {palsel, zx_dot ? zx_attr[3:0] : zx_attr[7:4]};
// 16c graphics
wire [3:0] hc_dot[0:3];
assign hc_dot[0] = data[ 7: 4];
assign hc_dot[1] = data[ 3: 0];
assign hc_dot[2] = data[15:12];
assign hc_dot[3] = data[11: 8];
wire [7:0] hc_pix = {palsel, hc_dot[psel[1:0]]};
// 256c graphics
wire [7:0] xc_dot[0:1];
assign xc_dot[0] = data[ 7: 0];
assign xc_dot[1] = data[15: 8];
wire [7:0] xc_pix = xc_dot[psel[0]];
// mode selects
wire [7:0] pix[0:3];
assign pix[R_ZX] = zx_pix; // ZX
assign pix[R_HC] = hc_pix; // 16c
assign pix[R_XC] = xc_pix; // 256c
assign pix[R_TX] = tx_pix; // text
wire pixv[0:3];
assign pixv[R_ZX] = zx_dot ^ (flash & zx_attr[7]);
assign pixv[R_HC] = |hc_dot[psel[1:0]];
assign pixv[R_XC] = |xc_dot[psel[0]];
assign pixv[R_TX] = zx_dot;
// video plex muxer
wire tsu_visible = (|tsdata_in[3:0] && !notsu);
wire gfx_visible = (pixv[render_mode] && !nogfx);
wire [7:0] video1 = tsu_visible ? tsdata_in : (nogfx ? border_in : pix[render_mode]);
wire [7:0] video2 = gfx_visible ? pix[render_mode] : (tsu_visible ? tsdata_in : border_in);
wire [7:0] video = hvpix ? (gfxovr ? video2 : video1) : ((hvtspix && tsu_visible) ? tsdata_in : border_in);
assign vplex_out = hires ? {temp, video[3:0]} : video; // in hi-res plex contains two pixels 4 bits each
reg [3:0] temp;
always @(posedge clk) if (c1) temp <= video[3:0];
endmodule
(
// clocks
input wire clk, c1,
// video controls
input wire hvpix,
input wire hvtspix,
input wire nogfx,
input wire notsu,
input wire gfxovr,
input wire flash,
input wire hires,
input wire [3:0] psel,
input wire [3:0] palsel,
// mode controls
input wire [1:0] render_mode,
// video data
input wire [31:0] data,
input wire [ 7:0] border_in,
input wire [ 7:0] tsdata_in,
output wire [ 7:0] vplex_out
);
localparam R_ZX = 2'h0;
localparam R_HC = 2'h1;
localparam R_XC = 2'h2;
localparam R_TX = 2'h3;
reg [3:0] temp;
// ZX graphics
wire [15:0] zx_gfx = data[15: 0];
wire [15:0] zx_atr = data[31:16];
wire zx_dot = zx_gfx[{psel[3], ~psel[2:0]}];
wire [7:0] zx_attr = ~psel[3] ? zx_atr[7:0] : zx_atr[15:8];
wire [7:0] zx_pix = {palsel, zx_attr[6], zx_dot ^ (flash & zx_attr[7]) ? zx_attr[2:0] : zx_attr[5:3]};
// text graphics
// (uses common renderer with ZX, but different attributes)
wire [7:0] tx_pix = {palsel, zx_dot ? zx_attr[3:0] : zx_attr[7:4]};
// 16c graphics
wire [3:0] hc_dot[0:3];
assign hc_dot[0] = data[ 7: 4];
assign hc_dot[1] = data[ 3: 0];
assign hc_dot[2] = data[15:12];
assign hc_dot[3] = data[11: 8];
wire [7:0] hc_pix = {palsel, hc_dot[psel[1:0]]};
// 256c graphics
wire [7:0] xc_dot[0:1];
assign xc_dot[0] = data[ 7: 0];
assign xc_dot[1] = data[15: 8];
wire [7:0] xc_pix = xc_dot[psel[0]];
// mode selects
wire [7:0] pix[0:3];
assign pix[R_ZX] = zx_pix; // ZX
assign pix[R_HC] = hc_pix; // 16c
assign pix[R_XC] = xc_pix; // 256c
assign pix[R_TX] = tx_pix; // text
wire pixv[0:3];
assign pixv[R_ZX] = zx_dot ^ (flash & zx_attr[7]);
assign pixv[R_HC] = |hc_dot[psel[1:0]];
assign pixv[R_XC] = |xc_dot[psel[0]];
assign pixv[R_TX] = zx_dot;
// video plex muxer
wire tsu_visible = (|tsdata_in[3:0] && !notsu);
wire gfx_visible = (pixv[render_mode] && !nogfx);
wire [7:0] video1 = tsu_visible ? tsdata_in : (nogfx ? border_in : pix[render_mode]);
wire [7:0] video2 = gfx_visible ? pix[render_mode] : (tsu_visible ? tsdata_in : border_in);
wire [7:0] video = hvpix ? (gfxovr ? video2 : video1) : ((hvtspix && tsu_visible) ? tsdata_in : border_in);
assign vplex_out = hires ? {temp, video[3:0]} : video; // in hi-res plex contains two pixels 4 bits each
always @(posedge clk) if (c1)
temp <= video[3:0];
endmodule

View File

@ -1,169 +1,243 @@
// This module generates all video raster signals
`include "tune.v"
// This module generates video raster signals
module video_sync
(
// clocks
input wire clk, f1, c0, c1, c3, pix_stb,
// video parameters
input wire [8:0] hpix_beg,
input wire [8:0] hpix_end,
input wire [8:0] vpix_beg,
input wire [8:0] vpix_end,
input wire [8:0] hpix_beg_ts,
input wire [8:0] hpix_end_ts,
input wire [8:0] vpix_beg_ts,
input wire [8:0] vpix_end_ts,
input wire [4:0] go_offs,
input wire [1:0] x_offs,
input wire [7:0] hint_beg,
input wire [8:0] vint_beg,
input wire [7:0] cstart,
input wire [8:0] rstart,
// video syncs
output reg hsync,
output reg vsync,
// video controls
input wire nogfx,
output wire v_pf,
output wire hpix,
output wire vpix,
output wire v_ts,
output wire hvpix,
output wire hvtspix,
output wire tv_hblank,
output wire tv_vblank,
output wire frame_start,
output wire line_start_s,
output wire pix_start,
output wire ts_start,
output wire frame,
output wire flash,
// video counters
output wire [8:0] ts_raddr,
output reg [8:0] lcount,
output reg [7:0] cnt_col,
output reg [8:0] cnt_row,
output reg cptr,
output reg [3:0] scnt,
// DRAM
input wire video_pre_next,
output reg video_go,
// ZX controls
input wire y_offs_wr,
output wire int_start
);
localparam HSYNC_BEG = 9'd11;
localparam HSYNC_END = 9'd43;
localparam HBLNK_BEG = 9'd00;
localparam HBLNK_END = 9'd88;
localparam HSYNCV_BEG = 9'd5;
localparam HSYNCV_END = 9'd31;
localparam HBLNKV_END = 9'd42;
localparam HPERIOD = 9'd448;
localparam VSYNC_BEG = 9'd08;
localparam VSYNC_END = 9'd11;
localparam VBLNK_BEG = 9'd00;
localparam VBLNK_END = 9'd32;
localparam VPERIOD = 9'd320;
// counters
reg [8:0] hcount = 0;
reg [8:0] vcount = 0;
// horizontal TV (7 MHz)
always @(posedge clk) if (c3) hcount <= line_start ? 9'b0 : hcount + 9'b1;
// vertical TV (15.625 kHz)
always @(posedge clk) if (line_start_s) vcount <= (vcount == (VPERIOD - 1)) ? 9'b0 : vcount + 9'b1;
// column address for DRAM
always @(posedge clk) begin
if (line_start2) begin
cnt_col <= cstart;
cptr <= 1'b0;
end
else if (video_pre_next) begin
cnt_col <= cnt_col + 8'b1;
cptr <= ~cptr;
end
end
// row address for DRAM
always @(posedge clk) begin if (c3)
if (vis_start || (line_start && y_offs_wr_r)) cnt_row <= rstart;
else if (line_start && vpix) cnt_row <= cnt_row + 9'b1;
end
// pixel counter
always @(posedge clk) if (pix_stb) scnt <= pix_start ? 4'b0 : scnt + 4'b1; // f1 or c3
// TS-line counter
assign ts_raddr = hcount - hpix_beg_ts;
always @(posedge clk) if (ts_start_coarse) lcount <= vcount - vpix_beg_ts + 9'b1;
// Y offset re-latch trigger
reg y_offs_wr_r;
always @(posedge clk) begin
if (y_offs_wr) y_offs_wr_r <= 1'b1;
else if (line_start_s) y_offs_wr_r <= 1'b0;
end
// FLASH generator
reg [4:0] flash_ctr;
assign frame = flash_ctr[0];
assign flash = flash_ctr[4];
always @(posedge clk) begin
if (frame_start && c3) begin
flash_ctr <= flash_ctr + 5'b1;
end
end
// sync strobes
wire hs = (hcount >= HSYNC_BEG) && (hcount < HSYNC_END);
wire vs = (vcount >= VSYNC_BEG) && (vcount < VSYNC_END);
assign tv_hblank = (hcount > HBLNK_BEG) && (hcount <= HBLNK_END);
assign tv_vblank = (vcount >= VBLNK_BEG) && (vcount < VBLNK_END);
assign hvpix = hpix && vpix;
assign hpix = (hcount >= hpix_beg) && (hcount < hpix_end);
assign vpix = (vcount >= vpix_beg) && (vcount < vpix_end);
(
// clocks
input wire clk, f1, c0, c3, pix_stb,
assign hvtspix = htspix && vtspix;
wire htspix = (hcount >= hpix_beg_ts) && (hcount < hpix_end_ts);
wire vtspix = (vcount >= vpix_beg_ts) && (vcount < vpix_end_ts);
// video parameters
input wire [8:0] hpix_beg,
input wire [8:0] hpix_end,
input wire [8:0] vpix_beg,
input wire [8:0] vpix_end,
input wire [8:0] hpix_beg_ts,
input wire [8:0] hpix_end_ts,
input wire [8:0] vpix_beg_ts,
input wire [8:0] vpix_end_ts,
input wire [4:0] go_offs,
input wire [1:0] x_offs,
input wire [7:0] hint_beg,
input wire [8:0] vint_beg,
input wire [7:0] cstart,
input wire [8:0] rstart,
assign v_ts = (vcount >= (vpix_beg_ts - 1)) && (vcount < (vpix_end_ts - 1)); // vertical TS window
assign v_pf = (vcount >= (vpix_beg_ts - 17)) && (vcount < (vpix_end_ts - 9)); // vertical tilemap prefetch window
always @(posedge clk) video_go <= (hcount >= (hpix_beg - go_offs - x_offs)) && (hcount < (hpix_end - go_offs - x_offs + 4)) && vpix && !nogfx;
wire line_start = hcount == (HPERIOD - 1);
assign line_start_s = line_start && c3;
wire line_start2 = hcount == (HSYNC_END - 1);
assign frame_start = line_start && (vcount == (VPERIOD - 1));
wire vis_start = line_start && (vcount == (VBLNK_END - 1));
assign pix_start = hcount == (hpix_beg - x_offs - 1);
wire ts_start_coarse = hcount == (hpix_beg_ts - 1);
assign ts_start = c3 && ts_start_coarse;
assign int_start = (hcount == {hint_beg, 1'b0}) && (vcount == vint_beg) && c0;
always @(posedge clk) begin
hsync <= hs;
vsync <= vs;
end
endmodule
// video syncs
output reg hsync,
output reg vsync,
output reg csync,
// video controls
input wire cfg_60hz,
input wire vga_on,
output reg v60hz = 0,
input wire nogfx,
output wire v_pf,
output wire hpix,
output wire vpix,
output wire v_ts,
output wire hvpix,
output wire hvtspix,
output wire tv_blank,
output wire vga_blank,
output wire vga_line,
output wire frame_start,
output wire line_start_s,
output wire pix_start,
output wire ts_start,
output wire frame,
output wire flash,
// video counters
output wire [9:0] vga_cnt_in,
output wire [9:0] vga_cnt_out,
output wire [8:0] ts_raddr,
output reg [8:0] lcount = 0,
output reg [7:0] cnt_col = 0,
output reg [8:0] cnt_row = 0,
output reg cptr = 0,
output reg [3:0] scnt = 0,
`ifdef PENT_312
output wire [4:0] hcnt,
output wire upper8,
`endif
// DRAM
input wire video_pre_next,
output reg video_go = 0,
// ZX controls
input wire y_offs_wr,
output wire int_start
);
localparam HSYNC_BEG = 9'd11;
localparam HSYNC_END = 9'd43;
localparam HBLNK_BEG = 9'd00;
localparam HBLNK_END = 9'd88;
localparam HSYNCV_BEG = 9'd5;
localparam HSYNCV_END = 9'd31;
localparam HBLNKV_END = 9'd42;
localparam HPERIOD = 9'd448;
localparam VSYNC_BEG_50 = 9'd08;
localparam VSYNC_END_50 = 9'd11;
localparam VBLNK_BEG_50 = 9'd00;
`ifdef PENT_312
localparam VBLNK_END_50 = 9'd24;
localparam VPERIOD_50 = 9'd312;
`else
localparam VBLNK_END_50 = 9'd32;
localparam VPERIOD_50 = 9'd320;
`endif
localparam VSYNC_BEG_60 = 9'd04;
localparam VSYNC_END_60 = 9'd07;
localparam VBLNK_BEG_60 = 9'd00;
localparam VBLNK_END_60 = 9'd22;
localparam VPERIOD_60 = 9'd262;
wire [8:0] vsync_beg = v60hz ? VSYNC_BEG_60 : VSYNC_BEG_50;
wire [8:0] vsync_end = v60hz ? VSYNC_END_60 : VSYNC_END_50;
wire [8:0] vblnk_beg = v60hz ? VBLNK_BEG_60 : VBLNK_BEG_50;
wire [8:0] vblnk_end = v60hz ? VBLNK_END_60 : VBLNK_END_50;
wire [8:0] vperiod = v60hz ? VPERIOD_60 : VPERIOD_50;
`ifdef PENT_312
assign hcnt = hcount[4:0];
assign upper8 = vcount < 8;
`endif
reg [8:0] hcount = 0;
reg [8:0] vcount = 0;
reg [8:0] cnt_out = 0;
reg vga_hblank = 0;
reg vga_vblank = 0;
wire line_start = hcount == (HPERIOD - 1);
wire line_start2 = hcount == (HSYNC_END - 1);
assign line_start_s = line_start && c3;
assign frame_start = line_start && (vcount == (vperiod - 1));
wire vis_start = line_start && (vcount == (vblnk_end - 1));
assign pix_start = hcount == (hpix_beg - x_offs - 1);
wire ts_start_coarse = hcount == (hpix_beg_ts - 1);
assign ts_start = c3 && ts_start_coarse;
assign int_start = (hcount == {hint_beg, 1'b0}) && (vcount == vint_beg) && c0;
wire vga_pix_start = ((hcount == (HBLNKV_END)) || (hcount == (HBLNKV_END + HPERIOD/2)));
wire hs_vga = ((hcount >= HSYNCV_BEG) && (hcount < HSYNCV_END)) || ((hcount >= (HSYNCV_BEG + HPERIOD/2)) && (hcount < (HSYNCV_END + HPERIOD/2)));
assign vga_line = (hcount >= HPERIOD/2);
wire hs = (hcount >= HSYNC_BEG) && (hcount < HSYNC_END);
wire vs = (vcount >= vsync_beg) && (vcount < vsync_end);
assign vga_cnt_in = {vcount[0], hcount - HBLNK_END};
assign vga_cnt_out = {~vcount[0], cnt_out};
wire tv_hblank = (hcount > HBLNK_BEG) && (hcount <= HBLNK_END);
wire tv_vblank = (vcount >= vblnk_beg) && (vcount < vblnk_end);
assign vga_blank = vga_hblank || vga_vblank;
assign tv_blank = tv_hblank || tv_vblank;
// horizontal TV (7 MHz)
always @(posedge clk) if (c3)
hcount <= line_start ? 9'b0 : hcount + 9'b1;
// vertical TV (15.625 kHz)
always @(posedge clk) if (line_start_s)
vcount <= (vcount == (vperiod - 1)) ? 9'b0 : vcount + 9'b1;
// horizontal VGA (14MHz)
always @(posedge clk) if (f1)
cnt_out <= vga_pix_start && c3 ? 9'b0 : cnt_out + 9'b1;
// column address for DRAM
always @(posedge clk)
begin
if (line_start2)
begin
cnt_col <= cstart;
cptr <= 1'b0;
end
else if (video_pre_next)
begin
cnt_col <= cnt_col + 8'b1;
cptr <= ~cptr;
end
end
// row address for DRAM
reg y_offs_wr_r;
always @(posedge clk) if (c3)
if (vis_start || (line_start && y_offs_wr_r))
cnt_row <= rstart;
else
if (line_start && vpix)
cnt_row <= cnt_row + 9'b1;
// pixel counter
always @(posedge clk) if (pix_stb) // f1 or c3
scnt <= pix_start ? 4'b0 : scnt + 4'b1;
// TS-line counter
assign ts_raddr = hcount - hpix_beg_ts;
always @(posedge clk)
if (ts_start_coarse)
lcount <= vcount - vpix_beg_ts + 9'b1;
// Y offset re-latch trigger
always @(posedge clk)
if (y_offs_wr)
y_offs_wr_r <= 1'b1;
else if (line_start_s)
y_offs_wr_r <= 1'b0;
// FLASH generator
reg [4:0] flash_ctr;
assign frame = flash_ctr[0];
assign flash = flash_ctr[4];
always @(posedge clk)
if (frame_start && c3)
begin
flash_ctr <= flash_ctr + 5'b1;
// re-sync 60Hz mode selector
`ifdef FORCE_60HZ
v60hz <= 1'b1;
`elsif ENABLE_60HZ
v60hz <= !cfg_60hz;
`else
v60hz <= 1'b0;
`endif
end
// sync strobes
wire vga_hblank1 = (cnt_out > 9'd359);
always @(posedge clk) if (f1) // fix me - bydlocode !!!
vga_hblank <= vga_hblank1;
assign hvpix = hpix && vpix;
assign hpix = (hcount >= hpix_beg) && (hcount < hpix_end);
assign vpix = (vcount >= vpix_beg) && (vcount < vpix_end);
wire htspix = (hcount >= hpix_beg_ts) && (hcount < hpix_end_ts);
wire vtspix = (vcount >= vpix_beg_ts) && (vcount < vpix_end_ts);
assign hvtspix = htspix && vtspix;
assign v_ts = (vcount >= (vpix_beg_ts - 1)) && (vcount < (vpix_end_ts - 1)); // vertical TS window
assign v_pf = (vcount >= (vpix_beg_ts - 17)) && (vcount < (vpix_end_ts - 9)); // vertical tilemap prefetch window
always @(posedge clk)
video_go <= (hcount >= (hpix_beg - go_offs - x_offs)) && (hcount < (hpix_end - go_offs - x_offs + 4)) && vpix && !nogfx;
always @(posedge clk) if (line_start_s) // fix me - bydlocode !!!
vga_vblank <= tv_vblank;
always @(posedge clk)
begin
hsync <= vga_on ? hs_vga : hs;
vsync <= vs;
csync <= ~(vs ^ hs);
end
endmodule

File diff suppressed because it is too large Load Diff

View File

@ -1,383 +1,459 @@
// This is the Tile Sprite Processing Unit
// Tiles map address:
// bits desc
// 20:13 tmpage
// 12:
// Graphics address:
// bits desc
// 20:13 Xgpage
// 15:7 line (bits 15:13 are added) - 512 lines
// 6:0 word within line - 128 words = 512 pixels
module video_ts
(
// clocks
input wire clk,
// video controls
input wire start,
input wire [8:0] line, // = vcount - vpix_beg + 9'b1;
input wire v_ts,
input wire v_pf, // vertical tilemap prefetch window
// video config
input wire [7:0] tsconf,
input wire [7:0] t0gpage,
input wire [7:0] t1gpage,
input wire [7:0] sgpage,
input wire [7:0] tmpage,
input wire [5:0] num_tiles,
input wire [8:0] t0x_offs,
input wire [8:0] t1x_offs,
input wire [8:0] t0y_offs,
input wire [8:0] t1y_offs,
input wire [1:0] t0_palsel,
input wire [1:0] t1_palsel,
// SFYS interface
input wire [7:0] sfile_addr_in,
input wire [15:0] sfile_data_in,
input wire sfile_we,
// renderer interface
output wire tsr_go,
output wire [5:0] tsr_addr, // graphics address within the line
output wire [8:0] tsr_line, // bitmap line
output wire [7:0] tsr_page, // bitmap 1st page
output wire [8:0] tsr_x, // addr in buffer (0-359 visibles)
output wire [2:0] tsr_xs, // size (8-64 pix)
output wire tsr_xf, // X flip
output wire [3:0] tsr_pal, // palette
input wire tsr_rdy, // renderer is done and ready to receive a new task
// DRAM interface
output wire [20:0] dram_addr,
output wire dram_req,
input wire dram_next,
input wire [15:0] dram_rdata
);
// config
wire s_en = tsconf[7];
wire t1_en = tsconf[6];
wire t0_en = tsconf[5];
wire t1z_en = tsconf[3];
wire t0z_en = tsconf[2];
// TS renderer interface
assign tsr_go = sprite_go || tile_go;
assign tsr_x = sprites ? sprites_x : tile_x;
assign tsr_xs = sprites ? sprites_xs : 3'd0;
assign tsr_xf = sprites ? sprites_xf : t_xflp;
assign tsr_page = sprites ? sgpage : tile_page;
assign tsr_line = sprites ? sprites_line : tile_line;
assign tsr_addr = sprites ? sprites_addr : tile_addr;
assign tsr_pal = sprites ? s_pal : tile_pal;
// Layer selectors control
localparam LAYERS = 6; // Total number of layers to process
localparam TM = 0; // Individual layers
localparam S0 = 1;
localparam T0 = 2;
localparam S1 = 3;
localparam T1 = 4;
localparam S2 = 5;
wire tmap = layer_active[TM];
wire sprites = layer_active[S0] || layer_active[S1] || layer_active[S2];
wire tiles = layer_active[T0] || layer_active[T1];
reg [LAYERS-1:0] layer;
always @(posedge clk)
if (start)
layer <= 1;
else if (|(layer & layer_skip))
layer <= {layer[LAYERS-2:0], 1'b0};
reg [LAYERS-1:0] layer_active;
always @(posedge clk)
if (start)
layer_active <= 0;
else
layer_active <= layer & ~layer_skip;
wire [LAYERS-1:0] layer_enabled = {s_en, t1_en, s_en, t0_en, s_en, t1_en || t0_en};
wire [LAYERS-1:0] layer_allowed = {{5{v_ts}}, v_pf};
wire [LAYERS-1:0] layer_end = {spr_end[2], tile_end[1], spr_end[1], tile_end[0], spr_end[0], tm_end};
reg [LAYERS-1:0] layer_skip;
always @(posedge clk)
if (start)
layer_skip <= ~(layer_enabled & layer_allowed);
else
layer_skip <= layer_skip | layer_end;
// --- Tile map prefetch ---
// DRAM controls
assign dram_addr = {tmpage, tm_b_row, tm_layer, tm_b_line, tm_num}; // 20:13 - page, 12:7 - row, 6 - layer, 5:0 - column (burst number : number in burst)
assign dram_req = tmap;
// TMB control
wire [8:0] tmb_waddr = {tm_line[4:3], tm_b_line, tm_num, tm_layer}; // 8:7 - buffer #, 6:4 - burst number (of 8 bursts), 3:1 - number in burst (8 tiles per burst), 0 - layer
wire [8:0] tm_line = line + 9'd16;
wire [2:0] tm_b_line = tm_line[2:0];
wire [5:0] tm_b_row = tm_line[8:3] + (tm_layer ? t1y_offs[8:3] : t0y_offs[8:3]);
wire [2:0] tm_num = tm_x[2:0];
wire tm_layer = tm_x[3];
// internal layers control
wire tm_end = tm_x == (t1_en ? 5'd16 : 5'd8);
wire tm_next = dram_next && tmap;
reg [1:0] m_layer;
always @(posedge clk)
if (start)
m_layer <= 2'b1;
else if (tm_end)
m_layer <= {m_layer[0], 1'b0};
// tilemap X coordinate
reg [4:0] tm_x;
always @(posedge clk)
if (start)
tm_x <= t0_en ? 5'd0 : 5'd8;
else if (tm_next)
tm_x <= tm_x + 5'd1;
// --- Tiles ---
// tile descriptor fields
wire [11:0] t_tnum = tmb_rdata[11:0];
wire [1:0] t_pal = tmb_rdata[13:12];
wire t_xflp = tmb_rdata[14];
wire t_yflp = tmb_rdata[15];
// TSR control
wire [7:0] tile_page = t_sel ? t0gpage : t1gpage;
wire [8:0] tile_line = {t_tnum[11:6], (t_line[2:0] ^ {3{t_yflp}})};
wire [5:0] tile_addr = t_tnum[5:0];
wire [8:0] tile_x = {(tx - 6'd1), 3'd0} - tx_offs[2:0];
wire [3:0] tile_pal = {t_sel ? t0_palsel : t1_palsel, t_pal};
// TMB control
wire [8:0] tmb_raddr = {t_line[4:3], tx + tx_offs[8:3], ~t_sel};
// layer parameter selectors
wire [8:0] tx_offs = t_sel ? t0x_offs : t1x_offs;
wire [3:0] ty_offs = t_sel ? t0y_offs[2:0] : t1y_offs[2:0];
wire t_sel = t_layer[0];
// internal layers control
wire [1:0] tile_end = {2{t_layer_end}} & t_layer[1:0];
wire t_layer_end = tx == num_tiles;
wire t_layer_start = start || t_layer_end;
reg [1:0] t_layer;
always @(posedge clk)
if (start)
t_layer <= t0_en ? 2'b01 : 2'b10;
else if (t_layer_end)
t_layer <= {t_layer[0], 1'b0};
// TMBUF control
// condition write to tx write to tm_valid
// t_layer_start 0 TM_PRE_VALID
// tm_pre_valid tx+1 TM_VALID
// tile_skip tx+1 -
// tile_go tx+1 TM_VALID
// tile_wait tx-1 TM_PRE_VALID
localparam TM_PRE_VALID = 2'b01;
localparam TM_VALID = 2'b10;
wire tile_go = tile_good && tsr_allowed;
wire tile_wait = tile_good && !tsr_allowed;
wire tile_good = tm_valid && tile_valid;
wire tile_skip = tm_valid && !tile_valid;
wire tsr_allowed = tiles && tsr_rdy;
wire tile_valid = |t_tnum || (t_sel ? t0z_en : t1z_en);
wire tm_pre_valid = tm_valid_r[0];
wire tm_valid = tm_valid_r[1];
reg [1:0] tm_valid_r;
always @(posedge clk)
if (t_layer_start || tile_wait)
tm_valid_r <= TM_PRE_VALID;
else if (tm_pre_valid || tile_go)
tm_valid_r <= TM_VALID;
reg [5:0] tx;
always @(posedge clk)
if (t_layer_start)
tx <= 6'd0;
else if (tm_pre_valid || tile_skip || tile_go)
tx <= tx + 6'd1;
else if (tile_wait)
tx <= tx - 6'd1;
// tile Y geometry
wire [4:0] t_line = line[4:0] + ty_offs;
// --- Sprites ---
// sprite descriptor fields
// R0
wire [8:0] s_ycrd = sfile_rdata[8:0];
wire [2:0] s_ysz = sfile_rdata[11:9];
wire s_act = sfile_rdata[13];
wire s_leap = sfile_rdata[14];
wire s_yflp = sfile_rdata[15];
// R1
wire [8:0] s_xcrd = sfile_rdata[8:0];
wire [2:0] s_xsz = sfile_rdata[11:9];
wire s_xflp = sfile_rdata[15];
// R2
wire [11:0] s_tnum = sfile_rdata[11:0];
wire [3:0] s_pal = sfile_rdata[15:12];
// TSR control
reg [8:0] sprites_x;
reg [2:0] sprites_xs;
reg sprites_xf;
wire [5:0] sprites_addr = s_tnum[5:0];
// internal layers control
wire [2:0] spr_end = ({3{s_layer_end}} & s_layer[2:0]) | {3{sprites_last}};
wire s_layer_end = (sr0_valid && !spr_valid && s_leap) || (sprite_go && s_leap_r);
wire sprites_last = sr0_valid && sprites_last_r;
reg sprites_last_r;
always @(posedge clk) sprites_last_r <= sreg == 8'd255;
reg [2:0] s_layer;
always @(posedge clk)
if (start)
s_layer <= 3'b1;
else if (s_layer_end)
s_layer <= {s_layer[1:0], 1'b0};
// SFile registers control
// condition write to sreg write to sr_valid action
// start 0 SR0_PRE_VALID Start
// sr0_pre_valid sreg+3 SR0_VALID SR0 pre-read
// sr0_valid && !spr_valid sreg+3 - Skip sprite
// sr0_valid && spr_valid sreg-2 SR1_PRE_VALID SR1 pre-read
// sr1_pre_valid sreg+1 SR1_VALID SR1 read
// sr1_valid sreg+1 SR2_VALID SR2 pre-read
// sr2_valid && !tsr_rdy - - Wait for TSR ready
// sr2_valid && tsr_rdy sreg+1 SR0_PRE_VALID Next sprite
// sprites_last - NONE_VALID End
localparam NONE_VALID = 5'b00000;
localparam SR0_PRE_VALID = 5'b00001;
localparam SR0_VALID = 5'b00010;
localparam SR1_PRE_VALID = 5'b00100;
localparam SR1_VALID = 5'b01000;
localparam SR2_VALID = 5'b10000;
wire sprite_go = sr2_valid && sprites && tsr_rdy; // kick to renderer
wire spr_valid = s_visible && s_act;
wire sr0_pre_valid = sr_valid[0];
wire sr0_valid = sr_valid[1];
wire sr1_pre_valid = sr_valid[2];
wire sr1_valid = sr_valid[3];
wire sr2_valid = sr_valid[4];
reg [4:0] sr_valid;
always @(posedge clk)
if (start)
sr_valid <= SR0_PRE_VALID;
else if (sprites_last)
sr_valid <= NONE_VALID;
else if (sr0_pre_valid)
sr_valid <= SR0_VALID;
else if (sr0_valid && spr_valid)
sr_valid <= SR1_PRE_VALID;
else if (sr1_pre_valid)
sr_valid <= SR1_VALID;
else if (sr1_valid)
sr_valid <= SR2_VALID;
else if (sprite_go)
sr_valid <= SR0_PRE_VALID;
reg [7:0] sreg;
always @(posedge clk)
if (start)
sreg <= 8'd0;
else if (sr0_pre_valid)
sreg <= sreg + 8'd3;
else if (sr0_valid)
sreg <= spr_valid ? (sreg - 8'd2) : (sreg + 8'd3);
else if (sr1_pre_valid || sprite_go)
sreg <= sreg + 8'd1;
// SFile control
reg [5:0] s_bmline_offset_r;
reg s_leap_r;
always @(posedge clk) begin
if (sr0_valid)
begin
s_leap_r <= s_leap;
s_bmline_offset_r <= s_bmline_offset;
end
if (sr1_valid)
begin
sprites_x <= s_xcrd;
sprites_xs <= s_xsz;
sprites_xf <= s_xflp;
end
end
// sprite Y geometry
wire [8:0] s_line = line - s_ycrd; // visible line of sprite in current video line
wire s_visible = (s_line <= s_ymax); // check if sprite line is within Y size
wire [5:0] s_ymax = {s_ysz, 3'b111};
wire [8:0] sprites_line = {s_tnum[11:6], 3'b0} + s_bmline_offset_r;
wire [5:0] s_bmline_offset = s_yflp ? (s_ymax - s_line[5:0]) : s_line[5:0];
// SFile
wire [15:0] sfile_rdata;
dpram #(.DATAWIDTH(16), .ADDRWIDTH(8)) video_sfile
`include "tune.v"
// This is the Tile Sprite Processing Unit
// Tiles map address:
// bits desc
// 20:13 tmpage
// 12:
// Graphics address:
// bits desc
// 20:13 Xgpage
// 15:7 line (bits 15:13 are added) - 512 lines
// 6:0 word within line - 128 words = 512 pixels
module video_ts
(
.clock (clk),
.address_a (sfile_addr_in),
.data_a (sfile_data_in),
.wren_a (sfile_we),
.address_b (sreg),
.q_b (sfile_rdata)
);
// 4 buffers * 2 tile-planes * 64 tiles * 16 bits (9x16) - used to prefetch tiles
// (2 altdprams)
wire [15:0] tmb_rdata;
dpram #(.DATAWIDTH(16), .ADDRWIDTH(9)) video_tmbuf
(
.clock (clk),
.address_a (tmb_waddr),
.data_a (dram_rdata),
.wren_a (tm_next),
.address_b (tmb_raddr),
.q_b (tmb_rdata)
// clocks
input wire clk,
// video controls
input wire start,
input wire [8:0] line, // = vcount - vpix_beg + 9'b1;
input wire v_ts,
input wire v_pf, // vertical tilemap prefetch window
// video config
input wire [7:0] tsconf,
input wire [7:0] t0gpage,
input wire [7:0] t1gpage,
input wire [7:0] sgpage,
input wire [7:0] tmpage,
input wire [5:0] num_tiles,
input wire [8:0] t0x_offs,
input wire [8:0] t1x_offs,
input wire [8:0] t0y_offs,
input wire [8:0] t1y_offs,
input wire [1:0] t0_palsel,
input wire [1:0] t1_palsel,
// SFYS interface
input wire [7:0] sfile_addr_in,
input wire [15:0] sfile_data_in,
input wire sfile_we,
// renderer interface
output wire tsr_go,
output wire [5:0] tsr_addr, // graphics address within the line
output wire [8:0] tsr_line, // bitmap line
output wire [7:0] tsr_page, // bitmap 1st page
output wire [8:0] tsr_x, // addr in buffer (0-359 visibles)
output wire [2:0] tsr_xs, // size (8-64 pix)
output wire tsr_xf, // X flip
output wire [3:0] tsr_pal, // palette
input wire tsr_rdy, // renderer is done and ready to receive a new task
// DRAM interface
output wire [20:0] dram_addr,
output wire dram_req,
input wire dram_next,
input wire [15:0] dram_rdata
);
endmodule
// config
wire s_en = tsconf[7];
wire t1_en = tsconf[6];
wire t0_en = tsconf[5];
wire t1z_en = tsconf[3];
wire t0z_en = tsconf[2];
// tile descriptor fields
wire [15:0] tmb_rdata;
wire [11:0] t_tnum = tmb_rdata[11:0];
wire [1:0] t_pal = tmb_rdata[13:12];
wire t_xflp = tmb_rdata[14];
wire t_yflp = tmb_rdata[15];
// Layer selectors control
localparam LAYERS = 6; // total number of layers to process
localparam TM = 0; // Individual layers
localparam S0 = 1;
localparam T0 = 2;
localparam S1 = 3;
localparam T1 = 4;
localparam S2 = 5;
reg [LAYERS-1:0] layer_active;
reg [LAYERS-1:0] layer_skip;
wire tmap = layer_active[TM];
wire sprites = layer_active[S0] || layer_active[S1] || layer_active[S2];
wire tiles = layer_active[T0] || layer_active[T1];
reg [LAYERS-1:0] layer;
always @(posedge clk)
if (start)
layer <= 1;
else if (|(layer & layer_skip))
layer <= {layer[LAYERS-2:0], 1'b0};
always @(posedge clk)
if (start)
layer_active <= 0;
else
layer_active <= layer & ~layer_skip;
wire [2:0] spr_end;
wire [1:0] tile_end;
wire tm_end;
wire [LAYERS-1:0] layer_enabled = {s_en, t1_en, s_en, t0_en, s_en, t1_en || t0_en};
wire [LAYERS-1:0] layer_allowed = {{5{v_ts}}, v_pf};
wire [LAYERS-1:0] layer_end = {spr_end[2], tile_end[1], spr_end[1], tile_end[0], spr_end[0], tm_end};
always @(posedge clk)
if (start)
layer_skip <= ~(layer_enabled & layer_allowed);
else
layer_skip <= layer_skip | layer_end;
// --- Tile map prefetch ---
// TMB control
reg [4:0] tm_x;
wire [8:0] tm_line = line + 9'd16;
wire [2:0] tm_b_line = tm_line[2:0];
wire [2:0] tm_num = tm_x[2:0];
wire tm_layer = tm_x[3];
wire [8:0] tmb_waddr = {tm_line[4:3], tm_b_line, tm_num, tm_layer}; // 8:7 - buffer #, 6:4 - burst number (of 8 bursts), 3:1 - number in burst (8 tiles per burst), 0 - layer
wire [5:0] tm_b_row = tm_line[8:3] + (tm_layer ? t1y_offs[8:3] : t0y_offs[8:3]);
// DRAM controls
assign dram_addr = {tmpage, tm_b_row, tm_layer, tm_b_line, tm_num}; // 20:13 - page, 12:7 - row, 6 - layer, 5:0 - column (burst number : number in burst)
assign dram_req = tmap;
// internal layers control
assign tm_end = tm_x == (t1_en ? 5'd16 : 5'd8);
wire tm_next = dram_next && tmap;
reg [1:0] m_layer;
always @(posedge clk)
if (start)
m_layer <= 2'b1;
else if (tm_end)
m_layer <= {m_layer[0], 1'b0};
// tilemap X coordinate
always @(posedge clk)
if (start)
tm_x <= t0_en ? 5'd0 : 5'd8;
else if (tm_next)
tm_x <= tm_x + 5'd1;
// --- Tiles ---
// layer parameter selectors
reg [1:0] t_layer;
reg [5:0] tx;
wire t_sel = t_layer[0];
wire [8:0] tx_offs = t_sel ? t0x_offs : t1x_offs;
wire [3:0] ty_offs = t_sel ? t0y_offs[2:0] : t1y_offs[2:0];
// TSR control
wire [4:0] t_line;
wire [7:0] tile_page = t_sel ? t0gpage : t1gpage;
wire [8:0] tile_line = {t_tnum[11:6], (t_line[2:0] ^ {3{t_yflp}})};
wire [5:0] tile_addr = t_tnum[5:0];
wire [8:0] tile_x = {(tx - 6'd1), 3'd0} - tx_offs[2:0];
wire [3:0] tile_pal = {t_sel ? t0_palsel : t1_palsel, t_pal};
// TMB control
wire [8:0] tmb_raddr = {t_line[4:3], tx + tx_offs[8:3], ~t_sel};
// internal layers control
wire t_layer_end = tx == num_tiles;
wire t_layer_start = start || t_layer_end;
assign tile_end = {2{t_layer_end}} & t_layer[1:0];
always @(posedge clk)
if (start)
t_layer <= t0_en ? 2'b01 : 2'b10;
else if (t_layer_end)
t_layer <= {t_layer[0], 1'b0};
// TMBUF control
// condition write to tx write to tm_valid
// t_layer_start 0 TM_PRE_VALID
// tm_pre_valid tx+1 TM_VALID
// tile_skip tx+1 -
// tile_go tx+1 TM_VALID
// tile_wait tx-1 TM_PRE_VALID
localparam TM_PRE_VALID = 2'b01;
localparam TM_VALID = 2'b10;
reg [1:0] tm_valid_r;
wire tm_valid = tm_valid_r[1];
wire tm_pre_valid = tm_valid_r[0];
wire tile_valid = |t_tnum || (t_sel ? t0z_en : t1z_en);
wire tsr_allowed = tiles && tsr_rdy;
wire tile_good = tm_valid && tile_valid;
wire tile_wait = tile_good && !tsr_allowed;
wire tile_skip = tm_valid && !tile_valid;
wire tile_go = tile_good && tsr_allowed;
always @(posedge clk)
if (t_layer_start || tile_wait)
tm_valid_r <= TM_PRE_VALID;
else if (tm_pre_valid || tile_go)
tm_valid_r <= TM_VALID;
always @(posedge clk)
if (t_layer_start)
tx <= 6'd0;
else if (tm_pre_valid || tile_skip || tile_go)
tx <= tx + 6'd1;
else if (tile_wait)
tx <= tx - 6'd1;
// tile Y geometry
assign t_line = line[4:0] + ty_offs;
// --- Sprites ---
// sprite descriptor fields
// R0
wire [15:0] sfile_rdata;
wire [8:0] s_ycrd = sfile_rdata[8:0];
wire [2:0] s_ysz = sfile_rdata[11:9];
wire s_act = sfile_rdata[13];
wire s_leap = sfile_rdata[14];
wire s_yflp = sfile_rdata[15];
// R1
wire [8:0] s_xcrd = sfile_rdata[8:0];
wire [2:0] s_xsz = sfile_rdata[11:9];
wire s_xflp = sfile_rdata[15];
// R2
wire [11:0] s_tnum = sfile_rdata[11:0];
wire [3:0] s_pal = sfile_rdata[15:12];
// TSR control
wire [5:0] sprites_addr = s_tnum[5:0];
// internal layers control
reg sprites_last_r;
reg [2:0] s_layer;
reg [7:0] sreg;
reg [4:0] sr_valid;
reg s_leap_r;
wire sr0_pre_valid = sr_valid[0];
wire sr0_valid = sr_valid[1];
wire sr1_pre_valid = sr_valid[2];
wire sr1_valid = sr_valid[3];
wire sr2_valid = sr_valid[4];
wire sprite_go;
wire s_visible;
wire spr_valid = s_visible && s_act;
wire s_layer_end = (sr0_valid && !spr_valid && s_leap) || (sprite_go && s_leap_r);
wire sprites_last = sr0_valid && sprites_last_r;
assign spr_end = ({3{s_layer_end}} & s_layer[2:0]) | {3{sprites_last}};
always @(posedge clk)
sprites_last_r <= sreg == 8'd255;
always @(posedge clk)
if (start)
s_layer <= 3'b1;
else if (s_layer_end)
s_layer <= {s_layer[1:0], 1'b0};
// SFile registers control
// condition write to sreg write to sr_valid action
// start 0 SR0_PRE_VALID Start
// sr0_pre_valid sreg+3 SR0_VALID SR0 pre-read
// sr0_valid && !spr_valid sreg+3 - Skip sprite
// sr0_valid && spr_valid sreg-2 SR1_PRE_VALID SR1 pre-read
// sr1_pre_valid sreg+1 SR1_VALID SR1 read
// sr1_valid sreg+1 SR2_VALID SR2 pre-read
// sr2_valid && !tsr_rdy - - Wait for TSR ready
// sr2_valid && tsr_rdy sreg+1 SR0_PRE_VALID Next sprite
// sprites_last - NONE_VALID End
localparam NONE_VALID = 5'b00000;
localparam SR0_PRE_VALID = 5'b00001;
localparam SR0_VALID = 5'b00010;
localparam SR1_PRE_VALID = 5'b00100;
localparam SR1_VALID = 5'b01000;
localparam SR2_VALID = 5'b10000;
assign sprite_go = sr2_valid && sprites && tsr_rdy; // a kick to renderer
always @(posedge clk)
if (start)
sr_valid <= SR0_PRE_VALID;
else if (sprites_last)
sr_valid <= NONE_VALID;
else if (sr0_pre_valid)
sr_valid <= SR0_VALID;
else if (sr0_valid && spr_valid)
sr_valid <= SR1_PRE_VALID;
else if (sr1_pre_valid)
sr_valid <= SR1_VALID;
else if (sr1_valid)
sr_valid <= SR2_VALID;
else if (sprite_go)
sr_valid <= SR0_PRE_VALID;
always @(posedge clk)
if (start)
sreg <= 8'd0;
else if (sr0_pre_valid)
sreg <= sreg + 8'd3;
else if (sr0_valid)
sreg <= spr_valid ? (sreg - 8'd2) : (sreg + 8'd3);
else if (sr1_pre_valid || sprite_go)
sreg <= sreg + 8'd1;
// sprite Y geometry
reg [5:0] s_bmline_offset_r;
wire [8:0] s_line = line - s_ycrd; // visible line of sprite in current video line
wire [5:0] s_ymax = {s_ysz, 3'b111};
assign s_visible = (s_line <= s_ymax); // check if sprite line is within Y size
wire [8:0] sprites_line = {s_tnum[11:6], 3'b0} + s_bmline_offset_r;
wire [5:0] s_bmline_offset = s_yflp ? (s_ymax - s_line[5:0]) : s_line[5:0];
// SFile control
reg [8:0] sprites_x;
reg [2:0] sprites_xs;
reg sprites_xf;
always @(posedge clk)
begin
if (sr0_valid)
begin
s_leap_r <= s_leap;
s_bmline_offset_r <= s_bmline_offset;
end
if (sr1_valid)
begin
sprites_x <= s_xcrd;
sprites_xs <= s_xsz;
sprites_xf <= s_xflp;
end
end
// SFile
dpram #(.DATAWIDTH(16), .ADDRWIDTH(8)) video_sfile
(
.clock (clk),
.address_a (sfile_addr_in),
.data_a (sfile_data_in),
.wren_a (sfile_we),
.address_b (sreg),
.q_b (sfile_rdata)
);
/*
altdpram video_sfile
(
.outclock (clk),
.wren (sfile_we),
.inclock (clk),
.data (sfile_data_in),
.rdaddress (sreg),
.wraddress (sfile_addr_in),
.q (sfile_rdata),
.aclr (1'b0),
.byteena (1'b1),
.inclocken (1'b1),
.outclocken (1'b1),
.rdaddressstall (1'b0),
.rden (1'b1),
.wraddressstall (1'b0)
);
defparam
video_sfile.indata_aclr = "OFF",
video_sfile.indata_reg = "INCLOCK",
video_sfile.intended_device_family = "ACEX1K",
video_sfile.lpm_type = "altdpram",
video_sfile.outdata_aclr = "OFF",
video_sfile.outdata_reg = "OUTCLOCK",
video_sfile.rdaddress_aclr = "OFF",
video_sfile.rdaddress_reg = "UNREGISTERED",
video_sfile.rdcontrol_aclr = "OFF",
video_sfile.rdcontrol_reg = "UNREGISTERED",
video_sfile.width = 16,
video_sfile.widthad = 8,
video_sfile.wraddress_aclr = "OFF",
video_sfile.wraddress_reg = "INCLOCK",
video_sfile.wrcontrol_aclr = "OFF",
video_sfile.wrcontrol_reg = "INCLOCK";
*/
// 4 buffers * 2 tile-planes * 64 tiles * 16 bits (9x16) - used to prefetch tiles
// (2 altdprams)
dpram #(.DATAWIDTH(16), .ADDRWIDTH(9)) video_tmbuf
(
.clock (clk),
.address_a (tmb_waddr),
.data_a (dram_rdata),
.wren_a (tm_next),
.address_b (tmb_raddr),
.q_b (tmb_rdata)
);
/*
altdpram video_tmbuf
(
.outclock (clk),
.wren (tm_next),
.inclock (clk),
.data (dram_rdata),
.rdaddress (tmb_raddr),
.wraddress (tmb_waddr),
.q (tmb_rdata),
.aclr (1'b0),
.byteena (1'b1),
.inclocken (1'b1),
.outclocken (1'b1),
.rdaddressstall (1'b0),
.rden (1'b1),
.wraddressstall (1'b0)
);
defparam
video_tmbuf.indata_aclr = "OFF",
video_tmbuf.indata_reg = "INCLOCK",
video_tmbuf.intended_device_family = "ACEX1K",
video_tmbuf.lpm_type = "altdpram",
video_tmbuf.outdata_aclr = "OFF",
video_tmbuf.outdata_reg = "OUTCLOCK",
video_tmbuf.rdaddress_aclr = "OFF",
video_tmbuf.rdaddress_reg = "UNREGISTERED",
video_tmbuf.rdcontrol_aclr = "OFF",
video_tmbuf.rdcontrol_reg = "UNREGISTERED",
video_tmbuf.width = 16,
video_tmbuf.widthad = 9,
video_tmbuf.wraddress_aclr = "OFF",
video_tmbuf.wraddress_reg = "INCLOCK",
video_tmbuf.wrcontrol_aclr = "OFF",
video_tmbuf.wrcontrol_reg = "INCLOCK";
*/
// TS renderer interface
assign tsr_go = sprite_go || tile_go;
assign tsr_x = sprites ? sprites_x : tile_x;
assign tsr_xs = sprites ? sprites_xs : 3'd0;
assign tsr_xf = sprites ? sprites_xf : t_xflp;
assign tsr_page = sprites ? sgpage : tile_page;
assign tsr_line = sprites ? sprites_line : tile_line;
assign tsr_addr = sprites ? sprites_addr : tile_addr;
assign tsr_pal = sprites ? s_pal : tile_pal;
endmodule

View File

@ -1,150 +1,150 @@
// This module renders pixels into TS-line for tiles/sprites
// Task execution is initiated by 'tsr_go' (one 'clk' period strobe).
// Inputs (only valid by 'tsr_go'):
// - DRAM address of graphics data (including page, line, word)
// - number of cycles to render (one cycle is 2 words = 8 pixels; 8 cycles = 64 pixels max)
// - X coordinate
// - X flip bit
// - palette selector
// Address in TS-line is calculated from X coordinate respecting the X flip.
// Inc/dec direction is set automatically.
// At the 'c2' of last DRAM cycle 'mem_rdy' is asserted.
// It should be used in comb to generate next cycle 'tsr_go' strobe for continuous renderer operation.
// First 'tsr_go' may be issued at any DRAM cycle, but the operation starts only
// after TS request recognized and processed by DRAM controller.
// It is recommended to assert 'tsr_go' at 'c2'.
`include "tune.v"
// This module renders pixels into TS-line for tiles/sprites
// Task execution is initiated by 'tsr_go' (one 'clk' period strobe).
// Inputs (only valid by 'tsr_go'):
// - DRAM address of graphics data (including page, line, word)
// - number of cycles to render (one cycle is 2 words = 8 pixels; 8 cycles = 64 pixels max)
// - X coordinate
// - X flip bit
// - palette selector
// Address in TS-line is calculated from X coordinate respecting the X flip.
// Inc/dec direction is set automatically.
// At the 'c2' of last DRAM cycle 'mem_rdy' is asserted.
// It should be used in comb to generate next cycle 'tsr_go' strobe for continuous renderer operation.
// First 'tsr_go' may be issued at any DRAM cycle, but the operation starts only
// after TS request recognized and processed by DRAM controller.
// It is recommended to assert 'tsr_go' at 'c2'.
module video_ts_render
(
// clocks
input wire clk,
// controls
input wire reset, // line start strobe, inits the machine
input wire [ 8:0] x_coord, // address in TS-line where render to, auto-flipped to match 'flip' times 'x_size'
input wire [ 2:0] x_size, // number of rendering cycles (each cycle is 8 pixels, 0 = 1 cycle)
input wire flip, // indicates that sprite is X-flipped
input wire tsr_go, // 1 clk 28MHz strobe for render start. Should be issued along with 'mem_rdy' for continuous process
input wire [ 5:0] addr, // address of dword within the line (dword = 8 pix)
input wire [ 8:0] line, // line of bitmap
input wire [ 7:0] page, // 1st page of bitmap (TxGpage or SGpage)
input wire [ 3:0] pal, // palette selector, bits[7:4] of CRAM address
output wire mem_rdy, // ready to receive new task
// TS-Line interface
output reg [ 8:0] ts_waddr,
output wire [ 7:0] ts_wdata,
output wire ts_we,
// DRAM interface
output wire [20:0] dram_addr,
output wire dram_req,
input wire [15:0] dram_rdata,
input wire dram_pre_next,
input wire dram_next
);
// DRAM request
assign dram_req = tsr_go || !mem_rdy;
// DRAM addressing
assign dram_addr = tsr_go ? addr_in : addr_next;
wire [20:0] addr_in = {addr_offset, addr, 1'b0};
wire [13:0] addr_offset = {page[7:3], line};
wire [20:0] addr_next = {addr_reg[20:7], addr_reg[6:0] + dram_next};
// as renderer can't move outside the single bitmap line, only 7 bits are processed
reg [20:0] addr_reg;
always @(posedge clk) addr_reg <= dram_addr;
// DRAM cycles counter
assign mem_rdy = cyc[4];
reg [4:0] cyc;
always @(posedge clk)
if (reset)
cyc <= 5'b10000;
else if (tsr_go)
cyc <= {1'b0, x_size, 1'b1};
else if (dram_pre_next)
cyc <= cyc - 5'd1;
// DRAM data fetching
reg [15:0] data;
always @(posedge clk) if (dram_next) data <= dram_rdata;
// pixel render counter
assign ts_we = render_on && |pix; // write signal for TS-line
wire render_on = !cnt[2];
reg [2:0] cnt;
always @(posedge clk)
if (reset)
cnt <= 3'b100;
else if (dram_next)
cnt <= 3'b000;
else if (render_on)
cnt <= cnt + 3'd1;
// renderer reload
reg tsr_rld;
always @(posedge clk)
if (reset)
tsr_rld <= 1'b0;
else if (tsr_go)
tsr_rld <= 1'b1;
else if (dram_next)
tsr_rld <= 1'b0;
// delayed values
reg [8:0] x_coord_d;
reg [3:0] pal_d;
reg flip_d;
always @(posedge clk)
if (tsr_go)
begin
x_coord_d <= x_coord + (flip ? {x_size, 3'b111} : 6'd0);
pal_d <= pal;
flip_d <= flip;
end
// TS-line address
wire [8:0] ts_waddr_mx = tsr_rld_stb ? x_coord_d : (render_on ? x_next : ts_waddr);
wire [8:0] x_next = ts_waddr + {{8{flip_r}}, 1'b1};
wire tsr_rld_stb = tsr_rld && dram_next;
always @(posedge clk) ts_waddr <= ts_waddr_mx;
reg [3:0] pal_r;
reg flip_r;
always @(posedge clk)
if (tsr_rld_stb)
begin
pal_r <= pal_d;
flip_r <= flip_d;
end
// renderer mux
assign ts_wdata = {pal_r, pix};
wire [3:0] pix = pix_m[cnt[1:0]];
wire [3:0] pix_m[0:3];
assign pix_m[0] = data[7:4];
assign pix_m[1] = data[3:0];
assign pix_m[2] = data[15:12];
assign pix_m[3] = data[11:8];
endmodule
(
// clocks
input wire clk,
// controls
input wire reset, // line start strobe, inits the machine
input wire [ 8:0] x_coord, // address in TS-line where render to, auto-flipped to match 'flip' times 'x_size'
input wire [ 2:0] x_size, // number of rendering cycles (each cycle is 8 pixels, 0 = 1 cycle)
input wire flip, // indicates that sprite is X-flipped
input wire tsr_go, // 1 clk 28MHz strobe for render start. Should be issued along with 'mem_rdy' for continuous process
input wire [ 5:0] addr, // address of dword within the line (dword = 8 pix)
input wire [ 8:0] line, // line of bitmap
input wire [ 7:0] page, // 1st page of bitmap (TxGpage or SGpage)
input wire [ 3:0] pal, // palette selector, bits[7:4] of CRAM address
output wire mem_rdy, // ready to receive new task
// TS-Line interface
output reg [ 8:0] ts_waddr,
output wire [ 7:0] ts_wdata,
output wire ts_we,
// DRAM interface
output wire [20:0] dram_addr,
output wire dram_req,
input wire [15:0] dram_rdata,
input wire dram_pre_next,
input wire dram_next
);
// renderer mux
reg [15:0] data;
reg [3:0] pal_r;
reg [2:0] pix_cnt;
wire [3:0] pix_m[0:3];
wire [3:0] pix = pix_m[pix_cnt[1:0]];
assign ts_wdata = {pal_r, pix};
assign pix_m[0] = data[7:4];
assign pix_m[1] = data[3:0];
assign pix_m[2] = data[15:12];
assign pix_m[3] = data[11:8];
// DRAM request
assign dram_req = tsr_go || !mem_rdy;
// DRAM addressing
reg [20:0] addr_reg;
wire [13:0] addr_offset = {page[7:3], line};
wire [20:0] addr_in = {addr_offset, addr, 1'b0};
wire [20:0] addr_next = {addr_reg[20:7], addr_reg[6:0] + dram_next};
assign dram_addr = tsr_go ? addr_in : addr_next;
always @(posedge clk)
addr_reg <= dram_addr;
// DRAM cycles counter
reg [4:0] cyc;
assign mem_rdy = cyc[4];
always @(posedge clk or posedge reset)
if (reset)
cyc <= 5'b10000;
else if (tsr_go)
cyc <= {1'b0, x_size, 1'b1};
else if (dram_pre_next)
cyc <= cyc - 5'd1;
// DRAM data fetching
always @(posedge clk)
if (dram_next)
data <= dram_rdata;
// pixel render counter
wire render_on = !pix_cnt[2];
assign ts_we = render_on && |pix; // write signal for TS-line
always @(posedge clk or posedge reset)
if (reset)
pix_cnt <= 3'b100;
else if (dram_next)
pix_cnt <= 3'b000;
else if (render_on)
pix_cnt <= pix_cnt + 3'd1;
// renderer reload
reg tsr_rld;
always @(posedge clk or posedge reset)
if (reset)
tsr_rld <= 1'b0;
else if (tsr_go)
tsr_rld <= 1'b1;
else if (dram_next)
tsr_rld <= 1'b0;
// delayed values
reg [8:0] x_coord_d;
reg [3:0] pal_d;
reg flip_d;
always @(posedge clk)
if (tsr_go)
begin
x_coord_d <= x_coord + (flip ? {x_size, 3'b111} : 6'd0);
pal_d <= pal;
flip_d <= flip;
end
// TS-line address
reg flip_r;
wire [8:0] x_next = ts_waddr + {{8{flip_r}}, 1'b1};
wire tsr_rld_stb = tsr_rld && dram_next;
wire [8:0] ts_waddr_mx = tsr_rld_stb ? x_coord_d : (render_on ? x_next : ts_waddr);
always @(posedge clk)
ts_waddr <= ts_waddr_mx;
always @(posedge clk)
if (tsr_rld_stb)
begin
pal_r <= pal_d;
flip_r <= flip_d;
end
endmodule