1
0
mirror of https://github.com/mist-devel/mist-board.git synced 2026-01-28 20:40:43 +00:00

[ATARI ST] Modular video subsystem, scandoubler made optional

This commit is contained in:
harbaum
2015-02-18 15:18:03 +00:00
parent 6745883a9c
commit 2583075eaf
10 changed files with 1426 additions and 1041 deletions

View File

@@ -323,6 +323,9 @@ set_global_assignment -name VERILOG_FILE clock.v
set_global_assignment -name VERILOG_FILE mist_top.v
set_global_assignment -name VERILOG_FILE user_io.v
set_global_assignment -name VERILOG_FILE video.v
set_global_assignment -name VERILOG_FILE shifter.v
set_global_assignment -name VERILOG_FILE scandoubler.v
set_global_assignment -name VERILOG_FILE sync_adjust.v
set_global_assignment -name VERILOG_FILE viking.v
set_global_assignment -name VERILOG_FILE video_modes.v
set_global_assignment -name VERILOG_FILE io_fifo.v

View File

@@ -240,7 +240,7 @@ wire [15:0] io_data_out = vreg_data_out | dma_data_out | blitter_data_out |
wire init = ~pll_locked;
/* -------------------------- Viking video card -------------------- */
/* -------------------------- Video subsystem -------------------- */
// viking/sm194 is enabled and max 8MB memory may be enabled. In steroids mode
// video memory is moved to $e80000 and all stram up to 14MB may be used
@@ -263,79 +263,47 @@ always @(posedge clk_32) begin
viking_in_use <= viking_in_use + 8'h01;
end
// video output multiplexer to switch between shifter and viking
wire viking_active = (viking_in_use == 8'hff) && !osd_enable;
assign VGA_HS = viking_active?viking_hs:shifter_hs;
assign VGA_VS = viking_active?viking_vs:shifter_vs;
assign VGA_R = viking_active?viking_r:shifter_r;
assign VGA_G = viking_active?viking_g:shifter_g;
assign VGA_B = viking_active?viking_b:shifter_b;
wire viking_hs, viking_vs;
wire [5:0] viking_r, viking_g, viking_b;
wire [22:0] viking_address;
wire viking_read;
viking viking (
.pclk (clk_128 ), // pixel
.bus_cycle (bus_cycle ),
// memory interface
.himem (steroids ),
.bclk (clk_8 ), // system bus clock = 8Mhz
.addr (viking_address ),
.data (ram_data_out_64),
.read (viking_read ),
// video output
.hs (viking_hs ),
.vs (viking_vs ),
.r (viking_r ),
.g (viking_g ),
.b (viking_b )
);
wire osd_enable;
wire shifter_hs, shifter_vs;
wire [5:0] shifter_r, shifter_g, shifter_b;
wire viking_active = (viking_in_use == 8'hff);
video video (
.clk (clk_32 ),
.clk_128 (clk_128 ),
.clk_32 (clk_32 ),
.bus_cycle (bus_cycle ),
// spi for OSD
.sdi (SPI_DI ),
.sck (SPI_SCK ),
.ss (SPI_SS3 ),
.osd_enable (osd_enable ),
// cpu register interface
.reg_clk (clk_8 ),
.reg_reset (reset ),
.reg_din (tg68_dat_out),
.reg_sel (vreg_sel ),
.reg_addr (tg68_adr[6:1]),
.reg_uds (tg68_uds ),
.reg_lds (tg68_lds ),
.reg_rw (tg68_rw ),
.reg_dout (vreg_data_out),
.cpu_clk (clk_8 ),
.cpu_reset (reset ),
.cpu_din (tg68_dat_out),
.cpu_sel (vreg_sel ),
.cpu_addr (tg68_adr[6:1]),
.cpu_uds (tg68_uds ),
.cpu_lds (tg68_lds ),
.cpu_rw (tg68_rw ),
.cpu_dout (vreg_data_out),
.vaddr (video_address ),
.data (ram_data_out_64),
.read (video_read ),
.hs (shifter_hs ),
.vs (shifter_vs ),
.video_r (shifter_r ),
.video_g (shifter_g ),
.video_b (shifter_b ),
.hs (VGA_HS ),
.vs (VGA_VS ),
.video_r (VGA_R ),
.video_g (VGA_G ),
.video_b (VGA_B ),
// configuration signals
.adjust (video_adj ),
.pal56 (~system_ctrl[9]),
.scanlines (system_ctrl[21:20]),
.ste (ste ),
.viking_enable ( viking_active ), // enable and activate viking video card
.viking_himem ( steroids ), // let viking use memory from $e80000
.scandoubler_disable ( scandoubler_disable ), // don't use scandoubler in 15khz modes
.adjust ( video_adj ),
.pal56 ( ~system_ctrl[9] ),
.scanlines ( system_ctrl[21:20] ),
.ste ( ste ),
// signals not affected by scan doubler required for
// irq generation
@@ -1110,25 +1078,25 @@ wire second_cpu_slot = (mste && enable_16mhz) || steroids;
// Two of the four cycles are being used. One for video (+STE audio) and one for
// cpu, DMA and Blitter. A third is optionally being used for faster CPU
wire video_cycle = (bus_cycle == 0);
wire video_cycle = (bus_cycle == 0)||(bus_cycle == 2);
wire cpu_cycle = (bus_cycle == 1) || (second_cpu_slot && (bus_cycle == 3));
wire viking_cycle = (bus_cycle == 2);
// ----------------- RAM address --------------
wire [22:0] video_cycle_addr = (st_hs && ste)?ste_dma_snd_addr:video_address;
wire ste_dma_has_bus = (bus_cycle == 0) && st_hs && ste;
wire [22:0] video_cycle_addr = ste_dma_has_bus?ste_dma_snd_addr:video_address;
wire [22:0] cpu_cycle_addr = dma_has_bus?dma_addr:(blitter_has_bus?blitter_master_addr:tg68_adr[23:1]);
wire [22:0] ram_address = viking_cycle?viking_address:(video_cycle?video_cycle_addr:cpu_cycle_addr);
wire [22:0] ram_address = video_cycle?video_cycle_addr:cpu_cycle_addr;
// ----------------- RAM read -----------------
// memory access during the video cycle is shared between video and ste_dma_snd
wire video_cycle_oe = (st_hs && ste)?ste_dma_snd_read:video_read;
wire video_cycle_oe = ste_dma_has_bus?ste_dma_snd_read:video_read;
// memory access during the cpu cycle is shared between blitter and cpu
wire cpu_cycle_oe = dma_has_bus?dma_read:(blitter_has_bus?blitter_master_read:(cpu_cycle && tg68_as && tg68_rw && cpu2mem));
wire ram_oe = viking_cycle?viking_read:(video_cycle?video_cycle_oe:(cpu_cycle?cpu_cycle_oe:1'b0));
wire ram_oe = video_cycle?video_cycle_oe:(cpu_cycle?cpu_cycle_oe:1'b0);
// ----------------- RAM write -----------------
wire cpu_cycle_wr = dma_has_bus?dma_write:(blitter_has_bus?blitter_master_write:(cpu_cycle && tg68_as && ~tg68_rw && cpu2ram));
wire ram_wr = (viking_cycle||video_cycle)?1'b0:(cpu_cycle?cpu_cycle_wr:1'b0);
wire ram_wr = video_cycle?1'b0:(cpu_cycle?cpu_cycle_wr:1'b0);
wire [15:0] ram_data_out;
wire [15:0] system_data_out = cpu2mem?ram_data_out:io_data_out;
@@ -1227,6 +1195,8 @@ wire eth_tx_read_strobe, eth_tx_read_begin;
wire [7:0] eth_rx_write_byte;
wire eth_rx_write_strobe, eth_rx_write_begin;
wire scandoubler_disable;
//// user io has an extra spi channel outside minimig core ////
user_io user_io(
// the spi interface
@@ -1274,6 +1244,9 @@ user_io user_io(
.eth_rx_write_begin (eth_rx_write_begin),
.eth_rx_write_strobe (eth_rx_write_strobe),
.eth_rx_write_byte (eth_rx_write_byte),
// io controller requests to disable vga scandoubler
.scandoubler_disable (scandoubler_disable),
.CORE_TYPE (8'ha3) // mist core id
);

View File

@@ -4,7 +4,7 @@
// On Screen Display implementation for the MiST board
// http://code.google.com/p/mist-board/
//
// Copyright (c) 2013 Till Harbaum <till@harbaum.org>
// Copyright (c) 2015 Till Harbaum <till@harbaum.org>
//
// This source file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
@@ -21,32 +21,74 @@
//
module osd (
input clk, // 31.875 MHz
input clk, // 31.875 MHz
// SPI interface for OSD
input sck,
input ss,
input sdi,
input sck,
input ss,
input sdi,
output reg osd_enable,
// current video beam position
input [9:0] hcnt,
input [9:0] vcnt,
input hs,
input vs,
input [5:0] in_r, // Red[5:0]
input [5:0] in_g, // Green[5:0]
input [5:0] in_b, // Blue[5:0]
input [5:0] r_in, // Red[5:0]
input [5:0] g_in, // Green[5:0]
input [5:0] b_in, // Blue[5:0]
output [5:0] out_r, // Red[5:0]
output [5:0] out_g, // Green[5:0]
output [5:0] out_b // Blue[5:0]
output [5:0] r_out, // Red[5:0]
output [5:0] g_out, // Green[5:0]
output [5:0] b_out // Blue[5:0]
);
assign out_r = !oe?in_r:{osd_pixel, osd_pixel, osd_pixel, in_r[5:3]};
assign out_g = !oe?in_g:{osd_pixel, osd_pixel, 1'b1, in_g[5:3]};
assign out_b = !oe?in_b:{osd_pixel, osd_pixel, osd_pixel, in_b[5:3]};
// combine input and OSD into a overlayed signal
assign r_out = !oe?r_in:{pixel, pixel, pixel, r_in[5:3]};
assign g_out = !oe?g_in:{pixel, pixel, 1'b1, g_in[5:3]};
assign b_out = !oe?b_in:{pixel, pixel, pixel, b_in[5:3]};
reg enabled;
// ---------------------------------------------------------------------------
// ------------------------- video timing analysis ---------------------------
// ---------------------------------------------------------------------------
// System clock is 32Mhz. Slowest hsync is 15Khz/64us. Hcounter must thus be
// able to run to 2048
reg [10:0] hcnt;
reg [10:0] vcnt;
reg [10:0] hs_high;
reg [10:0] hs_low;
wire hs_pol = hs_high < hs_low;
wire [10:0] dsp_width = hs_pol?hs_low:hs_high;
reg [10:0] vs_high;
reg [10:0] vs_low;
wire vs_pol = vs_high < vs_low;
wire [10:0] dsp_height = vs_pol?vs_low:vs_high;
reg hsD, vsD;
always @(negedge clk) begin
// check if hsync has changed
hsD <= hs;
if(hsD != hs) begin
if(hs) hs_low <= hcnt;
else hs_high <= hcnt;
hcnt <= 11'd0;
if(hs == hs_pol) begin
// check if vsync has changed
vsD <= vs;
if(vsD != vs) begin
if(vs) vs_low <= vcnt;
else vs_high <= vcnt;
vcnt <= 11'd0;
end else
vcnt <= vcnt + 11'd1;
end
end else
hcnt <= hcnt + 11'd1;
end
// ---------------------------------------------------------------------------
// -------------------------------- spi client -------------------------------
// ---------------------------------------------------------------------------
@@ -59,7 +101,7 @@ reg [7:0] cmd;
reg [4:0] cnt;
reg [10:0] bcnt;
reg [7:0] osd_buffer [2047:0]; // the OSD buffer itself
reg [7:0] buffer [2047:0]; // the OSD buffer itself
// the OSD has its own SPI interface to the io controller
always@(posedge sck, posedge ss) begin
@@ -83,44 +125,51 @@ always@(posedge sck, posedge ss) begin
// command 0x40: OSDCMDENABLE, OSDCMDDISABLE
if(sbuf[6:3] == 4'b0100)
osd_enable <= sdi;
enabled <= sdi;
end
// command 0x20: OSDCMDWRITE
if((cmd[7:3] == 5'b00100) && (cnt == 15)) begin
osd_buffer[bcnt] <= {sbuf[6:0], sdi};
buffer[bcnt] <= {sbuf[6:0], sdi};
bcnt <= bcnt + 11'd1;
end
end
end
localparam OSD_WIDTH = 10'd256;
localparam OSD_HEIGHT = 10'd128; // pixels are doubled vertically
// ---------------------------------------------------------------------------
// ------------------------------- OSD position ------------------------------
// ---------------------------------------------------------------------------
localparam OSD_POS_X = (10'd640 - OSD_WIDTH)>>1;
localparam OSD_POS_Y = (10'd400 - OSD_HEIGHT)>>1;
wire expand_x = ((dsp_width > 1000)&&(dsp_height < 1000))?1:0;
wire expand_y = (dsp_height > 400)?1:0;
wire [10:0] width = expand_x?10'd512:10'd256;
wire [10:0] height = expand_y?10'd128:10'd64;
localparam OSD_BORDER = 10'd2;
wire [10:0] border_x = expand_x?10'd4:10'd4;
wire [10:0] border_y = expand_y?10'd4:10'd2;
wire oe = osd_enable && (
(hcnt >= OSD_POS_X-OSD_BORDER) &&
(hcnt < (OSD_POS_X + OSD_WIDTH + OSD_BORDER)) &&
(vcnt >= OSD_POS_Y - OSD_BORDER) &&
(vcnt < (OSD_POS_Y + OSD_HEIGHT + OSD_BORDER)));
wire [10:0] pos_x = (dsp_width - width)>>1;
wire [10:0] pos_y = (dsp_height - height)>>1;
wire osd_content_area =
(hcnt >= OSD_POS_X) &&
(hcnt < (OSD_POS_X + OSD_WIDTH)) &&
(vcnt >= OSD_POS_Y) &&
(vcnt < (OSD_POS_Y + OSD_HEIGHT));
wire oe = enabled && (
(hcnt >= pos_x - border_x) &&
(hcnt < (pos_x + width + border_x)) &&
(vcnt >= pos_y - border_y) &&
(vcnt < (pos_y + height + border_y)));
wire [7:0] osd_hcnt = hcnt - OSD_POS_X + 7'd1; // one pixel offset for osd_byte register
wire [6:0] osd_vcnt = vcnt - OSD_POS_Y;
wire content_area =
(hcnt >= pos_x) && (hcnt < (pos_x + width - 1)) &&
(vcnt >= pos_y) && (vcnt < (pos_y + height - 1));
wire osd_pixel = osd_content_area?osd_byte[osd_vcnt[3:1]]:1'b0;
// one pixel offset for delay by byte register
wire [7:0] ihcnt = (expand_x?((hcnt-pos_x)>>1):(hcnt-pos_x))+8'd1;
wire [6:0] ivcnt = expand_y?((vcnt-pos_y)>>1):(vcnt-pos_y);
reg [7:0] osd_byte;
wire pixel = content_area?buffer_byte[ivcnt[2:0]]:1'b0;
reg [7:0] buffer_byte;
always @(posedge clk)
osd_byte <= osd_buffer[{osd_vcnt[6:4], osd_hcnt}];
buffer_byte <= buffer[{ivcnt[5:3], ihcnt}];
endmodule

167
cores/mist/scandoubler.v Normal file
View File

@@ -0,0 +1,167 @@
//
// scandoubler.v
//
// Copyright (c) 2015 Till Harbaum <till@harbaum.org>
//
// This source file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// TODO: Delay vsync one line
module scandoubler (
// system interface
input clk, // 31.875 MHz
input clk_16, // from shifter
// scanlines (00-none 01-25% 10-50% 11-75%)
input [1:0] scanlines,
// shifter video interface
input hs_in,
input vs_in,
input [3:0] r_in,
input [3:0] g_in,
input [3:0] b_in,
// output interface
output reg hs_out,
output reg vs_out,
output reg [3:0] r_out,
output reg [3:0] g_out,
output reg [3:0] b_out,
output is15k
);
// --------------------- create output signals -----------------
// latch everything once more to make it glitch free and apply scanline effect
reg scanline;
always @(posedge clk) begin
hs_out <= hs_sd;
vs_out <= vs_in;
// reset scanlines at every new screen
if(vs_out != vs_in)
scanline <= 1'b0;
// toggle scanlines at begin of every hsync
if(hs_out && !hs_sd)
scanline <= !scanline;
// if no scanlines or not a scanline
if(!scanline || scanlines == 2'b00) begin
r_out <= sd_out[11:8];
g_out <= sd_out[7:4];
b_out <= sd_out[3:0];
end else begin
case(scanlines)
2'b01: begin // reduce 25% = 1/2 + 1/4
r_out <= { 1'b0, sd_out[11:9] } + { 2'b00, sd_out[11:10] };
g_out <= { 1'b0, sd_out[7:5] } + { 2'b00, sd_out[7:6] };
b_out <= { 1'b0, sd_out[3:1] } + { 2'b00, sd_out[3:2] };
end
2'b10: begin // reduce 50% = 1/2
r_out <= { 1'b0, sd_out[11:9] };
g_out <= { 1'b0, sd_out[7:5] };
b_out <= { 1'b0, sd_out[3:1] };
end
2'b11: begin // reduce 75% = 1/4
r_out <= { 2'b00, sd_out[11:10] };
g_out <= { 2'b00, sd_out[7:6] };
b_out <= { 2'b00, sd_out[3:2] };
end
endcase
end // else: !if(!scanline || scanlines == 2'b00)
end
// scan doubler output register
reg [11:0] sd_out;
// ==================================================================
// ======================== the line buffers ========================
// ==================================================================
// 2 lines of 1024 pixels 3*4 bit RGB
reg [11:0] sd_buffer [2047:0];
// use alternating sd_buffers when storing/reading data
reg vsD;
reg line_toggle;
always @(negedge clk_16) begin
vsD <= vs_in;
if(vsD != vs_in)
line_toggle <= 1'b0;
// begin of incoming hsync
if(hsD && !hs_in)
line_toggle <= !line_toggle;
end
always @(negedge clk_16)
sd_buffer[{line_toggle, hcnt}] <= { r_in, g_in, b_in };
// ==================================================================
// =================== horizontal timing analysis ===================
// ==================================================================
// signal detection of 15khz if hsync frequency is less than 20KHz
assign is15k = hs_max > (16000000/20000);
// total hsync time (in 16MHz cycles), hs_total reaches 1024
reg [9:0] hs_max;
reg [9:0] hs_rise;
reg [9:0] hcnt;
reg hsD;
always @(negedge clk_16) begin
hsD <= hs_in;
// falling edge of hsync indicates start of line
if(hsD && !hs_in) begin
hs_max <= hcnt;
hcnt <= 10'd0;
end else
hcnt <= hcnt + 10'd1;
// save position of rising edge
if(!hsD && hs_in)
hs_rise <= hcnt;
end
// ==================================================================
// ==================== output timing generation ====================
// ==================================================================
reg [9:0] sd_hcnt;
reg hs_sd;
// timing generation runs 32 MHz (twice the input signal analysis speed)
always @(posedge clk) begin
// output counter synchronous to input and at twice the rate
sd_hcnt <= sd_hcnt + 10'd1;
if(hsD && !hs_in) sd_hcnt <= hs_max;
if(sd_hcnt == hs_max) sd_hcnt <= 10'd0;
// replicate horizontal sync at twice the speed
if(sd_hcnt == hs_max) hs_sd <= 1'b0;
if(sd_hcnt == hs_rise) hs_sd <= 1'b1;
// read data from line sd_buffer
sd_out <= sd_buffer[{~line_toggle, sd_hcnt}];
end
endmodule

704
cores/mist/shifter.v Normal file
View File

@@ -0,0 +1,704 @@
//
// shifter.v
//
// Atari ST(E) shifter implementation for the MiST board
// http://code.google.com/p/mist-board/
//
// Copyright (c) 2013-2015 Till Harbaum <till@harbaum.org>
//
// This source file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
module shifter (
// system interface
input clk, // 31.875 MHz
input [1:0] bus_cycle, // bus-cycle for sync
// memory interface
output reg [22:0] vaddr, // video word address counter
output read, // video read cycle
input [63:0] data, // video data read
// cpu register interface
input cpu_clk,
input cpu_reset,
input [15:0] cpu_din,
input cpu_sel,
input [5:0] cpu_addr,
input cpu_uds,
input cpu_lds,
input cpu_rw,
output reg [15:0] cpu_dout,
// screen interface
output reg hs, // H_SYNC
output reg vs, // V_SYNC
output reg [3:0] video_r, // Red
output reg [3:0] video_g, // Green
output reg [3:0] video_b, // Blue
// system config
input pal56, // use VGA compatible 56hz for PAL
input ste, // enable STE featurss
output vga_hs_pol, // sync polarity to be used on vga
output vga_vs_pol,
output clk_16, // 16Mhz clock for scan doubler
// signals not affected by scan doubler for internal use like irqs
output st_de,
output reg st_vs,
output reg st_hs
);
localparam STATE_SYNC = 2'd0;
localparam STATE_BLANK = 2'd1;
localparam STATE_BORDER = 2'd2;
localparam STATE_DISP = 2'd3;
// ---------------------------------------------------------------------------
// --------------------------- internal state counter ------------------------
// ---------------------------------------------------------------------------
reg [1:0] t;
always @(posedge clk) begin
// 32Mhz counter synchronous to 8 Mhz clock
// force counter to pass state 0 exactly after the rising edge of clk_reg (8Mhz)
if(((t == 2'd3) && ( cpu_clk == 0)) ||
((t == 2'd0) && ( cpu_clk == 1)) ||
((t != 2'd3) && (t != 2'd0)))
t <= t + 2'd1;
end
// give 16Mhz clock to scan doubler
assign clk_16 = t[0];
// create internal bus_cycle signal which is stable on the positive clock
// edge and extends the previous state by half a 32 Mhz clock cycle
reg [3:0] bus_cycle_L;
always @(negedge clk)
bus_cycle_L <= { bus_cycle, t };
// ---------------------------------------------------------------------------
// ------------------------------ internal signals ---------------------------
// ---------------------------------------------------------------------------
// st_de is the internal display enable signal as used by the mfp. This is used
// by software to generate a line interrupt and to e.g. do 512 color effects.
// st_de is active low. Using display enable (de) for this makes sure the cpu has
// plenty of time before data for the next line is starting to be fetched
assign st_de = ~de;
always @(posedge clk) begin
st_hs <= h_sync;
// hsync irq is generated after the rightmost border pixel column has been displayed
// hsync starts at begin of blanking phase
// if(hcnt == (t1_h_blank_right))
// st_hs <= 1'b1;
// hsync ends at begin of left border
// if(hcnt == (t4_h_border_left - 10'd16))
// st_hs <= 1'b0;
// vsync irq is generated right after the last border line has been displayed
// v_event is the begin of hsync. The hatari video.h says vbi happens 64 clock cycles
// ST hor counter runs at 16Mhz, thus the trigger is 128 events after h_sync
if(hcnt == v_event) begin
// vsync starts at begin of blanking phase
if(vcnt == t7_v_blank_bot + 10'd4) st_vs <= 1'b1;
// vsync ends at begin of top border
if(vcnt == t10_v_border_top + 10'd0) st_vs <= 1'b0;
end
end
// ---------------------------------------------------------------------------
// -------------------------------- video mode -------------------------------
// ---------------------------------------------------------------------------
wire [121:0] config_string;
video_modes video_modes(
// signals used to select the appropriate mode
.mono (mono),
.pal (pal),
.pal56 (pal56),
// resulting string containing timing values
.mode_str (config_string)
);
// The video config string contains 12 counter values (tX), six for horizontal
// timing and six for vertical timing. Each value has 10 bits, the total string
// is thus 120 bits long + space for some additional info like sync polarity
// display border blank(FP) sync blank(BP) border
// |--------------------|xxxxxx|#########|_______|##########|xxxxxx|
// t0 t1 t2 t3 t4 t5 horizontal
// t6 t7 t8 t9 t10 t11 vertical
// extract the various timing parameters from the config string
// horizontal timing values are for 640 pixel and are divided by 2 for 320 pixel low rez
assign vga_hs_pol = config_string[121];
wire [9:0] t0_h_border_right = low?{1'b0,config_string[120:112]}:config_string[120:111];
wire [9:0] t1_h_blank_right = low?{1'b0,config_string[110:102]}:config_string[110:101];
wire [9:0] t2_h_sync = low?{1'b0,config_string[100:92]}:config_string[100:91];
wire [9:0] t3_h_blank_left = low?{1'b0,config_string[90:82]}:config_string[90:81];
wire [9:0] t4_h_border_left = low?{1'b0,config_string[80:72]}:config_string[80:71];
wire [9:0] t5_h_end = low?{1'b0,config_string[70:62]}:config_string[70:61];
assign vga_vs_pol = config_string[60];
wire [9:0] t6_v_border_bot = config_string[59:50];
wire [9:0] t7_v_blank_bot = config_string[49:40];
wire [9:0] t8_v_sync = config_string[39:30];
wire [9:0] t9_v_blank_top = config_string[29:20];
wire [9:0] t10_v_border_top = config_string[19:10];
wire [9:0] t11_v_end = config_string[9:0];
// default video mode is monochrome
parameter DEFAULT_MODE = 3'd2;
// shiftmode register
reg [1:0] shmode;
wire mono = (shmode == 2'd2);
wire mid = (shmode == 2'd1);
wire low = (shmode == 2'd0);
// derive number of planes from shiftmode
wire [2:0] planes = mono?3'd1:(mid?3'd2:3'd4);
reg [1:0] syncmode;
reg [1:0] syncmode_latch;
wire pal = (syncmode_latch[1] == 1'b1);
// data input buffers for up to 4 planes
reg [15:0] data_latch[4];
localparam BASE_ADDR = 23'h8000; // default video base address 0x010000
reg [22:0] _v_bas_ad; // video base address register
// 16 colors with 3*4 bits each (4 bits for STE, ST only uses 3 bits)
reg [3:0] palette_r[15:0];
reg [3:0] palette_g[15:0];
reg [3:0] palette_b[15:0];
// STE-only registers
reg [7:0] line_offset; // number of words to skip at the end of each line
reg [3:0] pixel_offset; // number of pixels to skip at begin of line
reg ste_overscan_enable; // STE has a special 16 bit overscan
// ---------------------------------------------------------------------------
// ----------------------------- CPU register read ---------------------------
// ---------------------------------------------------------------------------
always @(cpu_sel, cpu_rw, cpu_uds, cpu_lds, cpu_addr, _v_bas_ad, shmode, vaddr,
syncmode, line_offset, pixel_offset, ste) begin
cpu_dout = 16'h0000;
// read registers
if(cpu_sel && cpu_rw) begin
// video base register (r/w)
if(cpu_addr == 6'h00) cpu_dout <= { 8'h00, _v_bas_ad[22:15] };
if(cpu_addr == 6'h01) cpu_dout <= { 8'h00, _v_bas_ad[14: 7] };
if(ste && cpu_addr == 6'h06) cpu_dout <= { 8'h00, _v_bas_ad[ 6: 0], 1'b0 };
// video address counter (ro on ST)
if(cpu_addr == 6'h02) cpu_dout <= { 8'h00, vaddr[22:15] };
if(cpu_addr == 6'h03) cpu_dout <= { 8'h00, vaddr[14:7 ] };
if(cpu_addr == 6'h04) cpu_dout <= { 8'h00, vaddr[6:0], 1'b0 };
// syncmode register
if(cpu_addr == 6'h05) cpu_dout <= { 6'h00, syncmode, 8'h00 };
if(ste) begin
if(cpu_addr == 6'h07) cpu_dout <= { 8'h00, line_offset };
if(cpu_addr == 6'h32) cpu_dout <= { 12'h000, pixel_offset };
end
// the color palette registers
if(cpu_addr >= 6'h20 && cpu_addr < 6'h30 ) begin
cpu_dout[3:0] <= palette_b[cpu_addr[3:0]];
cpu_dout[7:4] <= palette_g[cpu_addr[3:0]];
cpu_dout[11:8] <= palette_r[cpu_addr[3:0]];
// return only the 3 msb in non-ste mode
if(!ste) begin
cpu_dout[3] <= 1'b0;
cpu_dout[7] <= 1'b0;
cpu_dout[11] <= 1'b0;
end
end
// shift mode register
if(cpu_addr == 6'h30) cpu_dout <= { 6'h00, shmode, 8'h00 };
end
end
// ---------------------------------------------------------------------------
// ----------------------------- CPU register write --------------------------
// ---------------------------------------------------------------------------
// STE video address write signal is evaluated inside memory engine
wire ste_vaddr_write = ste && cpu_sel && !cpu_rw && !cpu_lds;
always @(negedge cpu_clk) begin
if(cpu_reset) begin
_v_bas_ad <= BASE_ADDR;
shmode <= DEFAULT_MODE; // default video mode 2 => mono
syncmode <= 2'b00; // 60hz
// disable STE hard scroll features
line_offset <= 8'h00;
pixel_offset <= 4'h0;
ste_overscan_enable <= 1'b0;
palette_b[ 0] <= 4'b111;
end else begin
// write registers
if(cpu_sel && !cpu_rw) begin
if(!cpu_lds) begin
// video base address hi/mid (ST and STE)
if(cpu_addr == 6'h00) _v_bas_ad[22:15] <= cpu_din[7:0];
if(cpu_addr == 6'h01) _v_bas_ad[14:7] <= cpu_din[7:0];
// In the STE setting hi or mid clears the low byte for ST compatibility
// in ST mode this doesn't harm
if(cpu_addr[5:1] == 5'h00) _v_bas_ad[6:0] <= 7'h00;
// the low byte can only be written in STE mode
if(ste && cpu_addr == 6'h06) _v_bas_ad[6:0] <= cpu_din[7:1];
end
// writing to sync mode toggles between 50 and 60 hz modes
if(cpu_addr == 6'h05 && !cpu_uds) syncmode <= cpu_din[9:8];
// writing special STE registers
if(ste && !cpu_lds) begin
if(cpu_addr == 6'h07) line_offset <= cpu_din[7:0];
if(cpu_addr == 6'h32) begin
pixel_offset <= cpu_din[3:0];
ste_overscan_enable <= 1'b0;
end
// Writing the video address counter happens directly inside the
// memory engine further below!!!
end
// byte write of 0 to ff8264 while ff8365 (pixel_offset) != 0 results in extra
// ste overscan
if(ste && !cpu_uds && cpu_lds) begin
if((cpu_addr == 6'h32) && (pixel_offset != 0))
ste_overscan_enable <= 1'b1;
end
// the color palette registers, always write bit 3 with zero if not in
// ste mode as this is the lsb of ste
if(cpu_addr >= 6'h20 && cpu_addr < 6'h30 ) begin
if(!cpu_uds) begin
if(!ste) palette_r[cpu_addr[3:0]] <= { 1'b0 , cpu_din[10:8] };
else palette_r[cpu_addr[3:0]] <= cpu_din[11:8];
end
if(!cpu_lds) begin
if(!ste) begin
palette_g[cpu_addr[3:0]] <= { 1'b0, cpu_din[6:4] };
palette_b[cpu_addr[3:0]] <= { 1'b0, cpu_din[2:0] };
end else begin
palette_g[cpu_addr[3:0]] <= cpu_din[7:4];
palette_b[cpu_addr[3:0]] <= cpu_din[3:0];
end
end
end
// make msb writeable if MiST video modes are enabled
if(cpu_addr == 6'h30 && !cpu_uds) shmode <= cpu_din[9:8];
end
end
end
// ---------------------------------------------------------------------------
// -------------------------- video signal generator -------------------------
// ---------------------------------------------------------------------------
// ----------------------- monochrome video signal ---------------------------
// mono uses the lsb of blue palette entry 0 to invert video
wire [3:0] blue0 = palette_b[0];
wire mono_bit = blue0[0]^shift_0[15];
wire [3:0] mono_rgb = { mono_bit, mono_bit, mono_bit, mono_bit };
// ------------------------- colour video signal -----------------------------
// For ST compatibility reasons the STE has the color bit order 0321. This is
// handled here
wire [3:0] color_index = border?4'd0:{ shift_3[15], shift_2[15], shift_1[15], shift_0[15] };
wire [3:0] color_r_pal = palette_r[color_index];
wire [3:0] color_r = { color_r_pal[2:0], color_r_pal[3] };
wire [3:0] color_g_pal = palette_g[color_index];
wire [3:0] color_g = { color_g_pal[2:0], color_g_pal[3] };
wire [3:0] color_b_pal = palette_b[color_index];
wire [3:0] color_b = { color_b_pal[2:0], color_b_pal[3] };
// --------------- de-multiplex color and mono into one vga signal -----------
wire [3:0] stvid_r = mono?mono_rgb:color_r;
wire [3:0] stvid_g = mono?mono_rgb:color_g;
wire [3:0] stvid_b = mono?mono_rgb:color_b;
// shift registers for up to 4 planes
reg [15:0] shift_0, shift_1, shift_2, shift_3;
// clock divider to generate the mid and low rez pixel clocks
wire pclk = low?t[1]:mid?t[0]:clk;
// use variable dot clock
always @(posedge pclk) begin
hs <= ~h_sync;
vs <= ~v_sync;
// drive video output
video_r <= blank?4'b0000:stvid_r;
video_g <= blank?4'b0000:stvid_g;
video_b <= blank?4'b0000:stvid_b;
// shift all planes and reload
// shift registers every 16 pixels
if((hcnt[3:0] == 4'hf)||(hcnt == t5_h_end)) begin
if(!ste || (pixel_offset == 0) || ste_overscan_enable) begin
shift_0 <= data_latch[0];
shift_1 <= data_latch[1];
shift_2 <= data_latch[2];
shift_3 <= data_latch[3];
end else begin
shift_0 <= ste_shifted_0;
shift_1 <= ste_shifted_1;
shift_2 <= ste_shifted_2;
shift_3 <= ste_shifted_3;
end
end else begin
shift_0 <= { shift_0[14:0], 1'b0 };
shift_1 <= { shift_1[14:0], 1'b0 };
shift_2 <= { shift_2[14:0], 1'b0 };
shift_3 <= { shift_3[14:0], 1'b0 };
end
end
// ---------------------------------------------------------------------------
// ----------------------------- overscan detection --------------------------
// ---------------------------------------------------------------------------
// Currently only opening the bottom border for overscan is supported. Opening
// the top border should also be easy. Opening the side borders is basically
// impossible as this requires a 100% perfect CPU and shifter timing.
reg last_syncmode;
reg [3:0] bottom_overscan_cnt;
reg [3:0] top_overscan_cnt;
wire bottom_overscan = (bottom_overscan_cnt != 0);
wire top_overscan = (top_overscan_cnt != 0);
always @(posedge clk) begin
if(cpu_reset) begin
top_overscan_cnt <= 4'd0;
bottom_overscan_cnt <= 4'd0;
end else begin
last_syncmode <= syncmode[1]; // delay syncmode to detect changes
// reset counters
if((vcnt == 0) && (hcnt == 10'd0)) begin
if(bottom_overscan_cnt != 0)
bottom_overscan_cnt <= bottom_overscan_cnt - 4'd1;
if(top_overscan_cnt != 0)
top_overscan_cnt <= top_overscan_cnt - 4'd1;
end
// this is the magic used to do "overscan".
// the magic actually involves more than writing zero (60hz)
// within line 200. But this is sufficient for our detection
// trigger in line 198/199
if((vcnt[8:1] == 8'd97)||(vcnt[8:1] == 8'd98)||(vcnt[8:1] == 8'd99)||
(vcnt[8:1] == 8'd100)||(vcnt[8:1] == 8'd101)) begin
// syncmode has changed from 1 to 0 (50 to 60 hz)
if((syncmode[1] == 1'b0) && (last_syncmode == 1'b1))
bottom_overscan_cnt <= 4'd15;
end
// trigger in line 284/285
if((vcnt[8:1] == 8'd133)||(vcnt[8:1] == 8'd134)||(vcnt[8:1] == 8'd135)||
(vcnt[8:1] == 8'd136)||(vcnt[8:1] == 8'd137)||(vcnt[8:1] == 8'd138)) begin
// syncmode has changed from 1 to 0 (50 to 60 hz)
if((syncmode[1] == 1'b0) && (last_syncmode == 1'b1))
top_overscan_cnt <= 4'd15;
end
end
end
// ---------------------------------------------------------------------------
// --------------------------- STE hard scroll shifter -----------------------
// ---------------------------------------------------------------------------
// When STE hard scrolling is being used (pixel_offset != 0) then memory reading starts
// 16 pixels earlier and data is being moved through an additional shift register
// extra 32 bit registers required for STE hard scrolling
reg [31:0] ste_shift_0, ste_shift_1, ste_shift_2, ste_shift_3;
// shifted data
wire [15:0] ste_shifted_0, ste_shifted_1, ste_shifted_2, ste_shifted_3;
// connect STE scroll shifters for each plane
ste_shifter ste_shifter_0 (
.skew (pixel_offset),
.in (ste_shift_0),
.out (ste_shifted_0)
);
ste_shifter ste_shifter_1 (
.skew (pixel_offset),
.in (ste_shift_1),
.out (ste_shifted_1)
);
ste_shifter ste_shifter_2 (
.skew (pixel_offset),
.in (ste_shift_2),
.out (ste_shifted_2)
);
ste_shifter ste_shifter_3 (
.skew (pixel_offset),
.in (ste_shift_3),
.out (ste_shifted_3)
);
// move data into STE hard scroll shift registers
always @(posedge clk) begin
if((bus_cycle_L == 4'd08) && (plane == 2'd0)) begin
// shift up 16 pixels and load new data into lower bits of shift registers
ste_shift_0 <= { ste_shift_0[15:0], data_latch[0] };
ste_shift_1 <= { ste_shift_1[15:0], (planes > 3'd1)?data_latch[1]:16'h0000 };
ste_shift_2 <= { ste_shift_2[15:0], (planes > 3'd2)?data_latch[2]:16'h0000 };
ste_shift_3 <= { ste_shift_3[15:0], (planes > 3'd2)?data_latch[3]:16'h0000 };
end
end
// ---------------------------------------------------------------------------
// ------------------------------- memory engine -----------------------------
// ---------------------------------------------------------------------------
assign read = (bus_cycle == 0) && de; // display enable can directly be used as a ram read signal
// current plane to be read from memory
reg [1:0] plane;
// To be able to output the first pixel we need to have one word for every plane already
// present in memory. We thus need a display enable signal which is (depending on color depth)
// 16, 32 or 64 pixel ahead of display enable
reg de, de_v;
// required pixel offset allowing for prefetch of 16 pixels
wire [9:0] ste_overscan = ste_overscan_enable?10'd16:10'd0;
// ste is starting another 16 pixels earlier if horizontal hard scroll is being used
wire [9:0] ste_prefetch = (ste && ((pixel_offset != 0) && !ste_overscan_enable))?10'd16:10'd0;
wire [9:0] de_h_start = t5_h_end - 10'd16 - ste_prefetch;
wire [9:0] de_h_end = t0_h_border_right - 10'd16 + ste_overscan;
// extra lines required by vertical overscan
wire [9:0] de_v_top_extra = top_overscan?10'd29:10'd0 /* synthesis keep */; // 29 extra ST lines at top
wire [9:0] de_v_bot_extra = bottom_overscan?10'd38:10'd0 /* synthesis keep */; // 38 extra ST lines at bottom
// calculate lines in which active display starts end ends
wire [9:0] de_v_start = t11_v_end - de_v_top_extra;
wire [9:0] de_v_end = t6_v_border_bot + de_v_bot_extra;
always @(posedge clk) begin
// line in which memory access is enabled
if(hcnt == v_event) begin
if(vcnt == de_v_start) de_v <= 1'b1;
if(vcnt == de_v_end) de_v <= 1'b0;
end
// display enable signal 16/32/64 bits (16*planes) ahead of display enable (de)
// include bus cycle to stay in sync in scna doubler mode
if(de_v) begin
if(hcnt == de_h_start) de <= 1'b1;
if(hcnt == de_h_end) de <= 1'b0;
end
// make sure each line starts with plane 0
if(hcnt == de_h_start)
plane <= 2'd0;
// according to hatari the video counter is reloaded 3 lines before
// the vbi occurs. This is right after the display has been painted.
// The video address counter is reloaded right after display ends
if((hcnt == t3_h_blank_left) && (vcnt == (t7_v_blank_bot+10'd1))) begin
vaddr <= _v_bas_ad;
// copy syncmode
syncmode_latch <= syncmode;
end else begin
// video transfer happens in cycle 3 (end of video cycle)
if(bus_cycle_L == 3) begin
// read if display enable is active
if(de) begin
// move incoming video data into data latch
// ST shifter only uses 16 out of possible 64 bits, so select the right word
case(vaddr[1:0])
2'd0: data_latch[plane] <= data[15: 0];
2'd1: data_latch[plane] <= data[31:16];
2'd2: data_latch[plane] <= data[47:32];
2'd3: data_latch[plane] <= data[63:48];
endcase
vaddr <= vaddr + 23'd1;
end
// advance plane counter
if(planes != 1) begin
plane <= plane + 2'd1;
if(plane == planes - 2'd1)
plane <= 2'd0;
end
end
end
// STE has additional ways to influence video address
if(ste) begin
// add line offset at the end of each video line
if(de_v && (hcnt == de_h_end) && (t == 0))
vaddr <= vaddr + line_offset;
// STE vaddr write handling
// bus_cycle 6 is in the middle of a cpu cycle
if((bus_cycle_L == 6) && ste_vaddr_write) begin
if(cpu_addr == 6'h02) vaddr[22:15] <= cpu_din[7:0];
if(cpu_addr == 6'h03) vaddr[14: 7] <= cpu_din[7:0];
if(cpu_addr == 6'h04) vaddr[ 6: 0] <= cpu_din[7:1];
end
end
end
// ---------------------------------------------------------------------------
// ------------------------- video timing generator --------------------------
// ---------------------------------------------------------------------------
reg [9:0] hcnt; // horizontal pixel counter
reg [1:0] h_state; // 0=sync, 1=blank, 2=border, 3=display
// A seperate vertical timing is not needed, vcnt[9:1] is the st line
reg [9:0] vcnt; // vertical line counter
reg [1:0] v_state; // 0=sync, 1=blank, 2=border, 3=display
// blank level is also used during sync
wire blank = (v_state == STATE_BLANK) || (h_state == STATE_BLANK) ||
(v_state == STATE_SYNC) || (h_state == STATE_SYNC);
// only the color modes use the border
wire border = (v_state == STATE_BORDER)||(h_state == STATE_BORDER);
// time in horizontal timing where vertical states change (at the begin of the sync phase)
wire [9:0] v_event = t2_h_sync;
reg v_sync, h_sync;
always @(posedge pclk) begin
// ------------- horizontal ST timing generation -------------
// Run st timing at full speed if no scan doubler is being used. Otherwise run
// it at half speed
if(hcnt == t5_h_end) begin
// sync hcnt to bus
if((low && (bus_cycle_L[3:2] == 2'b11)) ||
(mid && (bus_cycle_L[3:1] == 3'b111)) ||
(mono && (bus_cycle_L[3:0] == 4'b1111)))
hcnt <= 10'd0;
end else
hcnt <= hcnt + 10'd1;
if( hcnt == t2_h_sync) h_sync <= 1'b1;
if( hcnt == t3_h_blank_left) h_sync <= 1'b0;
// generate horizontal video signal states
if( hcnt == t2_h_sync ) h_state <= STATE_SYNC;
if((hcnt == t0_h_border_right + ste_overscan) ||
(hcnt == t4_h_border_left)) h_state <= STATE_BORDER;
if((hcnt == t1_h_blank_right) || (hcnt == t3_h_blank_left)) h_state <= STATE_BLANK;
if( hcnt == t5_h_end) h_state <= STATE_DISP;
// vertical state changes at begin of hsync
if(hcnt == v_event) begin
// ------------- vertical timing generation -------------
// increase vcnt
if(vcnt == t11_v_end) vcnt <= 10'd0;
else vcnt <= vcnt + 10'd1;
if( vcnt == t8_v_sync) v_sync <= 1'b1;
if( vcnt == t9_v_blank_top) v_sync <= 1'b0;
// generate vertical video signal states
if( vcnt == t8_v_sync ) v_state <= STATE_SYNC;
if((vcnt == de_v_end) || (vcnt == t10_v_border_top)) v_state <= STATE_BORDER;
if((vcnt == t7_v_blank_bot) || (vcnt == t9_v_blank_top)) v_state <= STATE_BLANK;
if( vcnt == de_v_start) v_state <= STATE_DISP;
end
end
endmodule
// ---------------------------------------------------------------------------
// --------------------------- STE hard scroll shifter -----------------------
// ---------------------------------------------------------------------------
module ste_shifter (
input [3:0] skew,
input [31:0] in,
output reg [15:0] out
);
always @(skew, in) begin
out = 16'h0000;
case(skew)
15: out = in[16:1];
14: out = in[17:2];
13: out = in[18:3];
12: out = in[19:4];
11: out = in[20:5];
10: out = in[21:6];
9: out = in[22:7];
8: out = in[23:8];
7: out = in[24:9];
6: out = in[25:10];
5: out = in[26:11];
4: out = in[27:12];
3: out = in[28:13];
2: out = in[29:14];
1: out = in[30:15];
0: out = in[31:16];
endcase; // case (skew)
end
endmodule

136
cores/mist/sync_adjust.v Normal file
View File

@@ -0,0 +1,136 @@
//
// sync_adjust.v
//
// Ajust the video sync position to allow the user to center the
// video on screen
//
// Copyright (c) 2015 Till Harbaum <till@harbaum.org>
//
// This source file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
module sync_adjust (
// system interface
input clk, // 31.875 MHz
input [15:0] adjust,
input hs_in,
input vs_in,
output reg hs_out,
output reg vs_out
);
// This has to cope with 15kHz (64us). At 32MHz a counter will count
// from 0 to 2047 in that time. Thus a 11 bit counter is required
// Extract and sign extend adjust values
wire signed [10:0] adjust_x = { {3{ adjust[15] }}, adjust[15:8] };
wire signed [9:0] adjust_y = { {2{ adjust[7] }}, adjust[7:0] };
// ==================================================================
// ====================== input timing analysis =====================
// ==================================================================
// total hsync time (in 32MHz cycles), hs_total reaches 2048
reg [10:0] hcnt;
reg hsD, vsD;
// hsync rise at hcnt == 0, signals relative to this:
reg [10:0] hs_rise;
reg [10:0] hs_fall;
reg [10:0] v_event;
// an event ecactly half a line delayed to v_event to generate
// vcntD which is stable over any change in v_event timinig
wire [10:0] hs_max_2 = { 1'b0, hs_max[10:1] };
wire [10:0] v_event_2 = (v_event > hs_max_2)?v_event-hs_max_2:v_event+hs_max_2;
reg [9:0] vcnt;
reg [9:0] vcntD; // delayed by half a line
reg [9:0] vs_rise;
reg [9:0] vs_fall;
// since the counter is restarted at the falling edge, hs_fall contains
// the max counter value (total times - 1)
wire [10:0] hs_max = hs_fall;
wire [9:0] vs_max = vs_fall;
always @(negedge clk) begin
hsD <= hs_in;
vsD <= vs_in;
// hsync has changed
hcnt <= hcnt + 11'd1;
if(hsD != hs_in) begin
if(!hs_in) begin
hcnt <= 11'd0;
hs_fall <= hcnt;
end else
hs_rise <= hcnt;
end
if(hcnt == v_event)
vcnt <= vcnt + 10'd1;
// vsync has changed
if(vsD != vs_in) begin
if(!vs_in) begin
v_event <= hcnt;
vcnt <= 10'd0;
vs_fall <= vcnt;
end else
vs_rise <= vcnt;
end
if(hcnt == v_event_2)
vcntD <= vcnt;
end
// ==================================================================
// ==================== output timing generation ====================
// ==================================================================
wire [10:0] hcnt_out_rst = (adjust_x > 0)?(adjust_x-10'd1):(hs_max+adjust_x);
reg [10:0] hcnt_out;
wire [9:0] vcnt_out_rst = (adjust_y > 0)?(adjust_y-9'd1):(vs_max+adjust_y);
reg [9:0] vcnt_out;
always @(posedge clk) begin
// generate new hcnt with offset
if(hcnt == hcnt_out_rst)
hcnt_out <= 11'd0;
else
hcnt_out <= hcnt_out + 11'd1;
// generate delayed hsync
if(hcnt_out == hs_rise) hs_out <= 1'b1;
if(hcnt_out == hs_fall) hs_out <= 1'b0;
// generate delayed vsync timing
if(hcnt_out == v_event) begin
if(vcntD == vcnt_out_rst)
vcnt_out <= 10'd0;
else
vcnt_out <= vcnt_out + 10'd1;
// generate delayed vsync
if(vcnt_out == vs_rise) vs_out <= 1'b1;
if(vcnt_out == vs_fall) vs_out <= 1'b0;
end
end
endmodule

View File

@@ -54,7 +54,8 @@ module user_io(
// on-board buttons and dip switches
output [1:0] BUTTONS,
output [1:0] SWITCHES
output [1:0] SWITCHES,
output scandoubler_disable
);
// filter spi clock. the 8 bit gate delay is ~2.5ns in total
@@ -65,13 +66,14 @@ reg [1:0] byte_cnt;
reg [6:0] sbuf;
reg [7:0] cmd;
reg [3:0] bit_cnt; // 0..15
reg [3:0] but_sw;
reg [4:0] but_sw;
// counter runs 0..7,8..15,8..15,8..15
wire [2:0] tx_bit = ~(bit_cnt[2:0]);
assign BUTTONS = but_sw[1:0];
assign SWITCHES = but_sw[3:2];
assign scandoubler_disable = but_sw[4];
always@(negedge spi_sck) begin
if(bit_cnt <= 7)
@@ -189,10 +191,8 @@ always@(negedge spi_sck) begin
if(bit_cnt == 15) begin
eth_mac_begin <= 1'b0;
if(cmd == 1) begin
but_sw[3:1] <= sbuf[2:0];
but_sw[0] <= SPI_MOSI;
end
if(cmd == 1)
but_sw <= { sbuf[3:0], SPI_MOSI };
// send ikbd byte to acia
if(cmd == 2) begin

File diff suppressed because it is too large Load Diff

View File

@@ -19,10 +19,13 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// video docs:
// VGA video docs:
// http://martin.hinner.info/vga/timing.html
// http://www.epanorama.net/faq/vga2rgb/calc.html
// Atari video timings:
// http://www.atari-forum.com/viewtopic.php?f=16&t=24855&start=350
// clocks on real sts:
// PAL 32084988 Hz
// NTSC 32042400 Hz
@@ -39,6 +42,9 @@
// vbl at cycle counter 64 (64 cycles after hbl)
// All video modes are based on a 32MHz pixel clock. This is two times the mid rez pixel clock and
// four times the low rez pixel clock.
module video_modes (
inout mono, // select monochrome mode (and not color)
input pal, // select pal mode (and not ntsc) if a color mode is selected
@@ -51,9 +57,6 @@ module video_modes (
// ---------------------------- generic timing parameters --------------------
// ---------------------------------------------------------------------------
localparam H_ACT = 10'd640;
localparam V_ACT = 10'd400;
// TIMING CONSTRAINTS:
// The total width (act+both blank+2*border+sync) must be a multiple of 16, for
// scan doubled modes a multiple of 8
@@ -63,15 +66,16 @@ localparam V_ACT = 10'd400;
// ---------------------------------------------------------------------------
// 56Hz replacement for Atari 50Hz low and medium resolution video mode scan doubled:
// displayed 640x200
// total: 928x612, active incl border: 720x560, displayed: 640x400
// horizontal scan rate: 17.27 kHz ST, 34.48 kHz VGA, vertical scan rate: 56.34 hz
wire [121:0] pal56_config_str;
conf pal56_conf(
// front porch sync width back porch border width sync polarity
.h_fp ( 10'd44), .h_s (10'd120), .h_bp ( 10'd44), .h_bd (10'd40), .h_sp (1'b1),
.v_fp ( 10'd24), .v_s ( 10'd4), .v_bp ( 10'd24), .v_tb (10'd80), .v_bb (10'd80), .v_sp (1'b1),
.str (pal56_config_str)
// display front porch sync width back porch border width sync polarity
.h_ds(10'd640), .h_fp( 10'd44), .h_s(10'd120), .h_bp( 10'd44), .h_lb(10'd40), .h_rb(10'd40), .h_sp(1'b1),
.v_ds(10'd200), .v_fp( 10'd12), .v_s( 10'd2), .v_bp( 10'd12), .v_tb(10'd40), .v_bb(10'd40), .v_sp(1'b1),
.str (pal56_config_str)
);
// ---------------------------------------------------------------------------
@@ -79,16 +83,20 @@ conf pal56_conf(
// ---------------------------------------------------------------------------
// Atari 50Hz low and medium resolution video mode scan doubled:
// According to troed: 40/40/28/320/72/12
// -> sync-80 bl-80 brd-56 dsp-640 brd-144 bl-24
// displayed 640x200
// total: 1024x626, active incl border: 800x560, displayed: 640x400
// horizontal scan rate: 15.625 kHz ST, 31.25 kHz VGA, vertical scan rate: 49.92 hz
// horizontal scan rate: 15.625 kHz ST (, 31.25 kHz VGA), vertical scan rate: 49.92 hz
wire [121:0] pal50_config_str;
conf pal50_conf(
// front porch sync width back porch border width sync polarity
.h_fp ( 10'd80), .h_s ( 10'd64), .h_bp ( 10'd80), .h_bd (10'd80), .h_sp (1'b1),
// .v_fp ( 10'd42), .v_s ( 10'd8), .v_bp ( 10'd42), .v_tb (10'd58), .v_bb (10'd76), .v_sp (1'b1),
.v_fp ( 10'd30), .v_s ( 10'd6), .v_bp ( 10'd30), .v_tb (10'd80), .v_bb (10'd80), .v_sp (1'b1),
.str (pal50_config_str)
// display front porch sync width back porch border width sync polarity
//.h_ds(10'd640), .h_fp( 10'd80), .h_s( 10'd64), .h_bp( 10'd80), .h_lb(10'd80), .h_rb(10'd80), .h_sp(1'b1),
.h_ds(10'd640), .h_fp( 10'd80), .h_s( 10'd80), .h_bp( 10'd24), .h_lb(10'd72), .h_rb(10'd128), .h_sp(1'b1),
// .h_ds(10'd640), .h_fp( 10'd80), .h_s( 10'd80), .h_bp( 10'd24), .h_lb(10'd56), .h_rb(10'd144), .h_sp(1'b1),
.v_ds(10'd200), .v_fp( 10'd15), .v_s( 10'd3), .v_bp( 10'd15), .v_tb(10'd40), .v_bb(10'd40), .v_sp(1'b1),
.str (pal50_config_str)
);
// ---------------------------------------------------------------------------
@@ -101,10 +109,10 @@ conf pal50_conf(
wire [121:0] ntsc_config_str;
conf ntsc_conf(
// front porch sync width back porch border width sync polarity
.h_fp ( 10'd76), .h_s ( 10'd64), .h_bp ( 10'd76), .h_bd (10'd80), .h_sp (1'b1),
.v_fp ( 10'd20), .v_s ( 10'd6), .v_bp ( 10'd20), .v_tb (10'd40), .v_bb (10'd40), .v_sp (1'b0),
.str (ntsc_config_str)
// display front porch sync width back porch border width sync polarity
.h_ds(10'd640), .h_fp( 10'd76), .h_s( 10'd64), .h_bp( 10'd76), .h_lb(10'd80), .h_rb(10'd80), .h_sp(1'b1),
.v_ds(10'd200), .v_fp( 10'd10), .v_s( 10'd3), .v_bp( 10'd10), .v_tb(10'd20), .v_bb(10'd20), .v_sp(1'b0),
.str (ntsc_config_str)
);
// ---------------------------------------------------------------------------
@@ -117,10 +125,10 @@ conf ntsc_conf(
wire [121:0] mono_config_str;
conf mono_conf(
// front porch sync width back porch border width sync polarity
.h_fp (10'd108), .h_s ( 10'd40), .h_bp (10'd108), .h_bd ( 10'd0), .h_sp (1'b0),
.v_fp ( 10'd48), .v_s ( 10'd5), .v_bp ( 10'd48), .v_tb ( 10'd0), .v_bb ( 10'd0), .v_sp (1'b0),
.str (mono_config_str)
// display front porch sync width back porch border width sync polarity
.h_ds( 10'd640), .h_fp(10'd108), .h_s( 10'd40), .h_bp(10'd108), .h_lb( 10'd0), .h_rb( 10'd0), .h_sp(1'b0),
.v_ds( 10'd400), .v_fp( 10'd48), .v_s( 10'd5), .v_bp( 10'd48), .v_tb( 10'd0), .v_bb( 10'd0), .v_sp(1'b0),
.str (mono_config_str)
);
@@ -132,44 +140,44 @@ endmodule
// ---------------------------------------------------------------------------
// ------------------ video timing config string generator -------------------
// ---------------------------------------------------------------------------
module conf (
input [9:0] h_fp, // horizontal front porch width
input [9:0] h_s, // horizontal sync width
input [9:0] h_bp, // horizontal back porch width
input [9:0] h_bd, // horizontal border width
input h_sp, // horizontal sync polarity
input [9:0] v_fp, // vertical front porch width
input [9:0] v_s, // vertical sync width
input [9:0] v_bp, // vertical back porch width
input [9:0] v_tb, // vertical border width top
input [9:0] v_bb, // vertical border width bottom
input v_sp, // vertical sync polarity
module conf (
input [9:0] h_ds, // horizontal display
input [9:0] h_fp, // horizontal front porch width
input [9:0] h_s, // horizontal sync width
input [9:0] h_bp, // horizontal back porch width
input [9:0] h_lb, // horizontal left border width
input [9:0] h_rb, // horizontal right border width
input h_sp, // horizontal sync polarity
input [9:0] v_ds, // vertical display
input [9:0] v_fp, // vertical front porch width
input [9:0] v_s, // vertical sync width
input [9:0] v_bp, // vertical back porch width
input [9:0] v_tb, // vertical top border width
input [9:0] v_bb, // vertical bottom border width
input v_sp, // vertical sync polarity
output [121:0] str
);
// all Atari video mods are based on a 640x400 screen
localparam H_ACT = 10'd640;
localparam V_ACT = 10'd400;
// all parameters are assembled into one config string
wire [60:0] h_str = { h_sp,
H_ACT - 10'd1,
H_ACT + h_bd - 10'd1,
H_ACT + h_bd + h_fp - 10'd1,
H_ACT + h_bd + h_fp + h_s - 10'd1,
H_ACT + h_bd + h_fp + h_s + h_bp - 10'd1,
H_ACT + h_bd + h_fp + h_s + h_bp + h_bd - 10'd1};
h_ds - 10'd1,
h_ds + h_rb - 10'd1,
h_ds + h_rb + h_fp - 10'd1,
h_ds + h_rb + h_fp + h_s - 10'd1,
h_ds + h_rb + h_fp + h_s + h_bp - 10'd1,
h_ds + h_rb + h_fp + h_s + h_bp + h_lb - 10'd1};
wire [60:0] v_str = { v_sp,
V_ACT - 10'd1,
V_ACT + v_bb - 10'd1,
V_ACT + v_bb + v_fp - 10'd1,
V_ACT + v_bb + v_fp + v_s - 10'd1,
V_ACT + v_bb + v_fp + v_s + v_bp - 10'd1,
V_ACT + v_bb + v_fp + v_s + v_bp + v_tb - 10'd1};
v_ds - 10'd1,
v_ds + v_bb - 10'd1,
v_ds + v_bb + v_fp - 10'd1,
v_ds + v_bb + v_fp + v_s - 10'd1,
v_ds + v_bb + v_fp + v_s + v_bp - 10'd1,
v_ds + v_bb + v_fp + v_s + v_bp + v_tb - 10'd1};
assign str = { h_str, v_str };
endmodule
endmodule

View File

@@ -4,7 +4,7 @@
// Atari ST(E) Viking/SM194
// http://code.google.com/p/mist-board/
//
// Copyright (c) 2013 Till Harbaum <till@harbaum.org>
// Copyright (c) 2013-2015 Till Harbaum <till@harbaum.org>
//
// This source file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
@@ -37,9 +37,9 @@ module viking (
// VGA output (multiplexed with sm124 output in top level)
output hs,
output vs,
output [5:0] r,
output [5:0] g,
output [5:0] b
output [3:0] r,
output [3:0] g,
output [3:0] b
);
localparam BASE = 23'h600000; // c00000
@@ -98,8 +98,8 @@ reg[10:0] h_cnt; // 0..2047
assign hs = ((h_cnt >= HBP1+H+HFP) && (h_cnt < HBP1+H+HFP+HS))?0:1;
always@(posedge pclk) begin
if(h_cnt==HBP1+H+HFP+HS+HBP2-1) begin
// make sure a line starts with the "viking" bus cyle (2)
// shifter has cycle 0, cpu has cycles 1 and 3
// make sure a line starts with the "video" bus cyle (0)
// cpu has cycles 1 and 3
if(bus_cycle_L == { 2'd1, 4'd15 })
h_cnt<=0;
end else
@@ -116,9 +116,6 @@ always@(posedge pclk) begin
end
end
// reorder words 1:2:3:4 -> 4:3:2:1
wire [63:0] data_reorder = { data[15:0], data[31:16], data[47:32], data[63:48] };
reg [63:0] input_latch;
reg [63:0] shift_register;
@@ -131,10 +128,13 @@ always@(posedge pclk) begin
addr <= addr + 23'd4; // advance 4 words (64 bits)
if(me && (bus_cycle_L == 6'h2f))
input_latch <= data_reorder;
input_latch <= data;
if(bus_cycle_L == 6'h3f)
shift_register <= input_latch;
// reorder words 1:2:3:4 -> 4:3:2:1
shift_register <=
{ input_latch[15:0], input_latch[31:16],
input_latch[47:32], input_latch[63:48] };
else
shift_register[63:1] <= shift_register[62:0];
end
@@ -146,10 +146,10 @@ wire de = (v_cnt < V)&&(h_cnt >= HBP1)&&(h_cnt < HBP1+H);
wire pix = de?(!shift_register[63]):1'b0;
// drive all 18 rgb bits from the data bit
wire [5:0] pix6 = { pix, pix, pix, pix, pix, pix };
assign r = pix6;
assign g = pix6;
assign b = pix6;
// drive all 12 rgb bits from the data bit
wire [3:0] pix4 = { pix, pix, pix, pix };
assign r = pix4;
assign g = pix4;
assign b = pix4;
endmodule